diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs b/rust/hg-core/src/dirstate_tree/dirstate_map.rs --- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs +++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs @@ -31,6 +31,13 @@ use crate::StatusOptions; /// 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 @@ pub struct DirstateMap<'on_disk> { /// 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 @@ impl<'on_disk> DirstateMap<'on_disk> { ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN], unreachable_bytes: 0, old_data_size: 0, + dirstate_version: DirstateVersion::V1, } } diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs @@ -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 @@ pub(super) fn read<'on_disk>( 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 @@ pub(super) fn read<'on_disk>( 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 --git a/rust/hg-core/src/dirstate_tree/status.rs b/rust/hg-core/src/dirstate_tree/status.rs --- a/rust/hg-core/src/dirstate_tree/status.rs +++ b/rust/hg-core/src/dirstate_tree/status.rs @@ -4,6 +4,7 @@ use crate::dirstate::status::StatusPath; 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 @@ pub fn status<'dirstate>( 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 @@ pub fn status<'dirstate>( 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