rust-status: don't trigger dirstate v1 rewrite when only v2 data is changed stable
authorRaphaël Gomès <rgomes@octobus.net>
Wed, 08 Jun 2022 19:15:58 +0200
branchstable
changeset 49337 6cd249556e20
parent 49320 3d3d7fc6035a
child 49344 0cc5f74ff7f0
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.
rust/hg-core/src/dirstate_tree/dirstate_map.rs
rust/hg-core/src/dirstate_tree/on_disk.rs
rust/hg-core/src/dirstate_tree/status.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,
         }
     }
 
--- 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<DirstateMap<'on_disk>, 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)
 }
--- 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