changeset 49150:f2ef6a4f918f stable

rhg: fix dirstate-v2 data file removal system In D12581 I introduced logic to remove the previous dirstate-v2 data file after a new one is created (and its corresponding docket), but the logic was flawed. I fixed it and made it simpler to understand by gather all logic in a single expression. Differential Revision: https://phab.mercurial-scm.org/D12586
author Raphaël Gomès <rgomes@octobus.net>
date Mon, 25 Apr 2022 16:45:03 +0200
parents 006688e36e12
children 1b6e381521c5
files rust/hg-core/src/repo.rs tests/test-dirstate.t
diffstat 2 files changed, 77 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-core/src/repo.rs	Tue Apr 19 12:27:40 2022 +0200
+++ b/rust/hg-core/src/repo.rs	Mon Apr 25 16:45:03 2022 +0200
@@ -435,25 +435,32 @@
         // it’s unset
         let parents = self.dirstate_parents()?;
         let (packed_dirstate, old_uuid_to_remove) = if self.has_dirstate_v2() {
-            let uuid = self.dirstate_data_file_uuid.get_or_init(self)?;
-            let mut uuid = uuid.as_ref();
-            let can_append = uuid.is_some();
+            let uuid_opt = self.dirstate_data_file_uuid.get_or_init(self)?;
+            let uuid_opt = uuid_opt.as_ref();
+            let can_append = uuid_opt.is_some();
             let (data, tree_metadata, append, old_data_size) =
                 map.pack_v2(can_append)?;
-            if !append {
-                uuid = None
-            }
-            let (uuid, old_uuid) = if let Some(uuid) = uuid {
-                let as_str = std::str::from_utf8(uuid)
-                    .map_err(|_| {
-                        HgError::corrupted("non-UTF-8 dirstate data file ID")
-                    })?
-                    .to_owned();
-                let old_uuid_to_remove = Some(as_str.to_owned());
-                (as_str, old_uuid_to_remove)
-            } else {
-                (DirstateDocket::new_uid(), None)
+
+            // Reuse the uuid, or generate a new one, keeping the old for
+            // deletion.
+            let (uuid, old_uuid) = match uuid_opt {
+                Some(uuid) => {
+                    let as_str = std::str::from_utf8(uuid)
+                        .map_err(|_| {
+                            HgError::corrupted(
+                                "non-UTF-8 dirstate data file ID",
+                            )
+                        })?
+                        .to_owned();
+                    if append {
+                        (as_str, None)
+                    } else {
+                        (DirstateDocket::new_uid(), Some(as_str))
+                    }
+                }
+                None => (DirstateDocket::new_uid(), None),
             };
+
             let data_filename = format!("dirstate.{}", uuid);
             let data_filename = self.hg_vfs().join(data_filename);
             let mut options = std::fs::OpenOptions::new();
--- a/tests/test-dirstate.t	Tue Apr 19 12:27:40 2022 +0200
+++ b/tests/test-dirstate.t	Mon Apr 25 16:45:03 2022 +0200
@@ -120,4 +120,58 @@
   C hgext3rd/__init__.py
 
   $ cd ..
+
+Check that the old dirstate data file is removed correctly and the new one is
+valid.
+
+  $ dirstate_data_files () {
+  >   find .hg -maxdepth 1 -name "dirstate.*"
+  > }
+
+  $ find_dirstate_uuid () {
+  >   dirstate_data_files | sed 's#.hg/dirstate.##'
+  > }
+
+  $ dirstate_uuid_has_not_changed () {
+  >   # Pure Python always rewrites the whole dirstate
+  >   if [ $# -eq 1 ] || [ "$HGMODULEPOLICY" = *"rust"* ] || [ -n "$RHG_INSTALLED_AS_HG" ]; then
+  >     test $current_uid = $(find_dirstate_uuid)
+  >   fi
+  > }
+
+  $ cd ..
+  $ hg init append-mostly
+  $ cd append-mostly
+  $ mkdir dir dir2
+  $ touch dir/a dir/b dir/c dir/d dir/e dir2/f
+  $ hg commit -Aqm initial
+  $ hg st
+  $ dirstate_data_files | wc -l
+   *1 (re)
+  $ current_uid=$(find_dirstate_uuid)
+
+Nothing changes here
+
+  $ hg st
+  $ dirstate_data_files | wc -l
+   *1 (re)
+  $ dirstate_uuid_has_not_changed
+
+Trigger an append with a small change
+
+  $ echo "modified" > dir2/f
+  $ hg st
+  M dir2/f
+  $ dirstate_data_files | wc -l
+   *1 (re)
+  $ dirstate_uuid_has_not_changed
+
+Delete most of the dirstate to trigger a non-append
+  $ hg rm dir/a dir/b dir/c dir/d
+  $ dirstate_data_files | wc -l
+   *1 (re)
+  $ dirstate_uuid_has_not_changed also-if-python
+  [1]
+
+  $ cd ..
 #endif