repo-upgrade: write new requirement before upgrading the dirstate stable
authorRaphaël Gomès <rgomes@octobus.net>
Tue, 02 May 2023 15:40:13 +0200
branchstable
changeset 50363 b4b1791f36e4
parent 50362 51041a1a4c59
child 50364 f930af431193
repo-upgrade: write new requirement before upgrading the dirstate This will prevent a small race condition where another hg process still believes the repo is dirstate-v1 during the upgrade process. This is good to have, but it is not a proper fix for the underlying problem. There is code that assumes a requirement means a usage, e.g. having the `generaldelta` requirement would imply *all* revlogs to use general delta, but it's not true, it simply means that the repository advertises to the client it needs to understand `generaldelta` in order to read the repo. In the case of the dirstate, having the requirement *technically* should always be the same as using dirstate-v2, since there is only one dirstate and requirements should be as minimal as possible. However, we should not assume this and make the code more robust in a future patch (series).
mercurial/upgrade_utils/engine.py
--- a/mercurial/upgrade_utils/engine.py	Wed Apr 26 15:30:35 2023 -0400
+++ b/mercurial/upgrade_utils/engine.py	Tue May 02 15:40:13 2023 +0200
@@ -655,9 +655,14 @@
             pass
 
     assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
+    use_v2 = new == b'v2'
+    if use_v2:
+        # Write the requirements *before* upgrading
+        scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
+
     srcrepo.dirstate._map.preload()
-    srcrepo.dirstate._use_dirstate_v2 = new == b'v2'
-    srcrepo.dirstate._map._use_dirstate_v2 = srcrepo.dirstate._use_dirstate_v2
+    srcrepo.dirstate._use_dirstate_v2 = use_v2
+    srcrepo.dirstate._map._use_dirstate_v2 = use_v2
     srcrepo.dirstate._dirty = True
     try:
         srcrepo.vfs.unlink(b'dirstate')
@@ -667,8 +672,9 @@
         pass
 
     srcrepo.dirstate.write(None)
-
-    scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
+    if not use_v2:
+        # Remove the v2 requirement *after* downgrading
+        scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
 
 
 def upgrade_tracked_hint(ui, srcrepo, upgrade_op, add):