Patchwork D11866: dirstate-v2: fix upgrade on an empty repository

login
register
mail settings
Submitter phabricator
Date Dec. 7, 2021, 8:51 a.m.
Message ID <differential-rev-PHID-DREV-f4t7fwro7deor3pnxmb6-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/50192/
State Superseded
Headers show

Comments

phabricator - Dec. 7, 2021, 8:51 a.m.
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This used to crash as the dirstate file does not exist in this case.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11866

AFFECTED FILES
  mercurial/upgrade_utils/engine.py
  tests/test-upgrade-repo.t

CHANGE DETAILS




To: marmoute, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/tests/test-upgrade-repo.t b/tests/test-upgrade-repo.t
--- a/tests/test-upgrade-repo.t
+++ b/tests/test-upgrade-repo.t
@@ -1701,3 +1701,66 @@ 
   $ hg debugformat -v | grep dirstate-v2
   dirstate-v2:         no     no      no
   $ hg status
+
+  $ cd ..
+
+dirstate-v2: upgrade and downgrade from and empty repository:
+-------------------------------------------------------------
+
+  $ hg init --config format.exp-rc-dirstate-v2=no dirstate-v2-empty
+  $ cd dirstate-v2-empty
+  $ hg debugformat | grep dirstate-v2
+  dirstate-v2:         no
+
+upgrade
+
+  $ hg debugupgraderepo --run --config format.exp-rc-dirstate-v2=yes
+  upgrade will perform the following actions:
+  
+  requirements
+     preserved: * (glob)
+     added: dirstate-v2
+  
+  dirstate-v2
+     "hg status" will be faster
+  
+  processed revlogs:
+    - all-filelogs
+    - changelog
+    - manifest
+  
+  beginning upgrade...
+  repository locked and read-only
+  creating temporary repository to stage upgraded data: $TESTTMP/dirstate-v2-empty/.hg/upgrade.* (glob)
+  (it is safe to interrupt this process any time before data migration completes)
+  upgrading to dirstate-v2 from v1
+  replaced files will be backed up at $TESTTMP/dirstate-v2-empty/.hg/upgradebackup.* (glob)
+  removing temporary repository $TESTTMP/dirstate-v2-empty/.hg/upgrade.* (glob)
+  $ hg debugformat | grep dirstate-v2
+  dirstate-v2:        yes
+
+downgrade
+
+  $ hg debugupgraderepo --run --config format.exp-rc-dirstate-v2=no
+  upgrade will perform the following actions:
+  
+  requirements
+     preserved: * (glob)
+     removed: dirstate-v2
+  
+  processed revlogs:
+    - all-filelogs
+    - changelog
+    - manifest
+  
+  beginning upgrade...
+  repository locked and read-only
+  creating temporary repository to stage upgraded data: $TESTTMP/dirstate-v2-empty/.hg/upgrade.* (glob)
+  (it is safe to interrupt this process any time before data migration completes)
+  downgrading from dirstate-v2 to v1
+  replaced files will be backed up at $TESTTMP/dirstate-v2-empty/.hg/upgradebackup.* (glob)
+  removing temporary repository $TESTTMP/dirstate-v2-empty/.hg/upgrade.* (glob)
+  $ hg debugformat | grep dirstate-v2
+  dirstate-v2:         no
+
+  $ cd ..
diff --git a/mercurial/upgrade_utils/engine.py b/mercurial/upgrade_utils/engine.py
--- a/mercurial/upgrade_utils/engine.py
+++ b/mercurial/upgrade_utils/engine.py
@@ -7,6 +7,7 @@ 
 
 from __future__ import absolute_import
 
+import errno
 import stat
 
 from ..i18n import _
@@ -633,16 +634,29 @@ 
         util.copyfile(
             srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
         )
-        util.copyfile(
-            srcrepo.vfs.join(b'dirstate'), backupvfs.join(b'dirstate')
-        )
+        try:
+            util.copyfile(
+                srcrepo.vfs.join(b'dirstate'), backupvfs.join(b'dirstate')
+            )
+        except (IOError, OSError) as e:
+            # The dirstate does not exist on an empty repo or a repo with no
+            # revision checked out
+            if e.errno != errno.ENOENT:
+                raise
 
     assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
     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._dirty = True
-    srcrepo.vfs.unlink(b'dirstate')
+    try:
+        srcrepo.vfs.unlink(b'dirstate')
+    except (IOError, OSError) as e:
+        # The dirstate does not exist on an empty repo or a repo with no
+        # revision checked out
+        if e.errno != errno.ENOENT:
+            raise
+
     srcrepo.dirstate.write(None)
 
     scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)