# HG changeset patch # User Raphaël Gomès # Date 1654708558 -7200 # Node ID 6cd249556e207d96949c5ddb304f3bda276e6540 # Parent 3d3d7fc6035a26e6280a0d7efbd0d31f93b56bf6 rust-status: don't trigger dirstate v1 rewrite when only v2 data is changed The assumption that we need to rewrite (or append to) the dirstate if the ignore pattern hash has changed or if any cached directory mtimes have changed is only valid when using dirstate-v2. In dirstate-v1, neither of these things are written to disk. diff -r 3d3d7fc6035a -r 6cd249556e20 rust/hg-core/src/dirstate_tree/dirstate_map.rs --- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs Tue Jun 14 04:04:08 2022 +0200 +++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs Wed Jun 08 19:15:58 2022 +0200 @@ -31,6 +31,13 @@ /// anymore) is less than this fraction of the total amount of existing data. const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5; +#[derive(Debug, PartialEq, Eq)] +/// Version of the on-disk format +pub enum DirstateVersion { + V1, + V2, +} + pub struct DirstateMap<'on_disk> { /// Contents of the `.hg/dirstate` file pub(super) on_disk: &'on_disk [u8], @@ -53,6 +60,8 @@ /// Size of the data used to first load this `DirstateMap`. Used in case /// we need to write some new metadata, but no new data on disk. pub(super) old_data_size: usize, + + pub(super) dirstate_version: DirstateVersion, } /// Using a plain `HgPathBuf` of the full path from the repository root as a @@ -434,6 +443,7 @@ ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN], unreachable_bytes: 0, old_data_size: 0, + dirstate_version: DirstateVersion::V1, } } diff -r 3d3d7fc6035a -r 6cd249556e20 rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs Tue Jun 14 04:04:08 2022 +0200 +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs Wed Jun 08 19:15:58 2022 +0200 @@ -3,6 +3,7 @@ //! See `mercurial/helptext/internals/dirstate-v2.txt` use crate::dirstate::TruncatedTimestamp; +use crate::dirstate_tree::dirstate_map::DirstateVersion; use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef}; use crate::dirstate_tree::path_with_basename::WithBasename; use crate::errors::HgError; @@ -276,7 +277,9 @@ metadata: &[u8], ) -> Result, DirstateV2ParseError> { if on_disk.is_empty() { - return Ok(DirstateMap::empty(on_disk)); + let mut map = DirstateMap::empty(on_disk); + map.dirstate_version = DirstateVersion::V2; + return Ok(map); } let (meta, _) = TreeMetadata::from_bytes(metadata) .map_err(|_| DirstateV2ParseError)?; @@ -291,6 +294,7 @@ ignore_patterns_hash: meta.ignore_patterns_hash, unreachable_bytes: meta.unreachable_bytes.get(), old_data_size: on_disk.len(), + dirstate_version: DirstateVersion::V2, }; Ok(dirstate_map) } diff -r 3d3d7fc6035a -r 6cd249556e20 rust/hg-core/src/dirstate_tree/status.rs --- a/rust/hg-core/src/dirstate_tree/status.rs Tue Jun 14 04:04:08 2022 +0200 +++ b/rust/hg-core/src/dirstate_tree/status.rs Wed Jun 08 19:15:58 2022 +0200 @@ -4,6 +4,7 @@ use crate::dirstate_tree::dirstate_map::BorrowedPath; use crate::dirstate_tree::dirstate_map::ChildNodesRef; use crate::dirstate_tree::dirstate_map::DirstateMap; +use crate::dirstate_tree::dirstate_map::DirstateVersion; use crate::dirstate_tree::dirstate_map::NodeData; use crate::dirstate_tree::dirstate_map::NodeRef; use crate::dirstate_tree::on_disk::DirstateV2ParseError; @@ -61,16 +62,29 @@ let (ignore_fn, warnings, patterns_changed): (IgnoreFnType, _, _) = if options.list_ignored || options.list_unknown { - let mut hasher = Sha1::new(); - let (ignore_fn, warnings) = get_ignore_function( - ignore_files, - &root_dir, - &mut |pattern_bytes| hasher.update(pattern_bytes), - )?; - let new_hash = *hasher.finalize().as_ref(); - let changed = new_hash != dmap.ignore_patterns_hash; - dmap.ignore_patterns_hash = new_hash; - (ignore_fn, warnings, Some(changed)) + let (ignore_fn, warnings, changed) = match dmap.dirstate_version { + DirstateVersion::V1 => { + let (ignore_fn, warnings) = get_ignore_function( + ignore_files, + &root_dir, + &mut |_pattern_bytes| {}, + )?; + (ignore_fn, warnings, None) + } + DirstateVersion::V2 => { + let mut hasher = Sha1::new(); + let (ignore_fn, warnings) = get_ignore_function( + ignore_files, + &root_dir, + &mut |pattern_bytes| hasher.update(pattern_bytes), + )?; + let new_hash = *hasher.finalize().as_ref(); + let changed = new_hash != dmap.ignore_patterns_hash; + dmap.ignore_patterns_hash = new_hash; + (ignore_fn, warnings, Some(changed)) + } + }; + (ignore_fn, warnings, changed) } else { (Box::new(|&_| true), vec![], None) }; @@ -135,7 +149,8 @@ outcome.dirty = common.ignore_patterns_have_changed == Some(true) || !outdated.is_empty() - || !new_cachable.is_empty(); + || (!new_cachable.is_empty() + && dmap.dirstate_version == DirstateVersion::V2); // Remove outdated mtimes before adding new mtimes, in case a given // directory is both