[PATCH 2 of 2] rollback: only restore dirstate (et. al.) when appropriate

Greg Ward greg-hg at gerg.ca
Thu Sep 15 21:22:10 CDT 2011


# HG changeset patch
# User Greg Ward <greg at gerg.ca>
# Date 1316139696 14400
# Node ID b33ba776a9977a4b1460eebb5acd9dc954da03c5
# Parent  23fe96275fabc99c33f61da305f81948ea5c61df
rollback: only restore dirstate (et. al.) when appropriate.

If the working dir parent is not one of the changesets slated for
destruction, then this rollback should not affect the working dir:
that means do not restore dirstate, branch, or bookmarks.

This is very preliminary, just posted for initial review. Some tests
still fail.

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -769,33 +769,49 @@
 
     def _rollback(self, dryrun):
         try:
-            args = self.opener.read("undo.desc").splitlines()
-            if len(args) >= 3 and self.ui.verbose:
-                desc = _("repository tip rolled back to revision %s"
-                         " (undo %s: %s)\n") % (
-                         int(args[0]) - 1, args[1], args[2])
+            args = self.opener.read('undo.desc').splitlines()
+            if len(args) >= 3:
+                (oldlen, desc, detail) = args
             elif len(args) >= 2:
-                desc = _("repository tip rolled back to revision %s"
-                         " (undo %s)\n") % (
-                         int(args[0]) - 1, args[1])
+                (oldlen, desc) = args
+                detail = None
+            oldlen = int(oldlen)
+            oldtip = oldlen - 1
+            doomed = range(oldlen, len(self))
+
+            if detail and self.ui.verbose:
+                msg = (_('repository tip rolled back to revision %s'
+                         ' (undo %s: %s)\n')
+                       % (oldtip, desc, detail))
+            else:
+                msg = (_('repository tip rolled back to revision %s'
+                         ' (undo %s)\n')
+                       % (oldtip, desc))
         except IOError:
-            desc = _("rolling back unknown transaction\n")
-        self.ui.status(desc)
+            doomed = None       # not enough info
+            msg = _('rolling back unknown transaction\n')
+        self.ui.status(msg)
         if dryrun:
             return 0
+
+        parents = map(self.changelog.rev, self.dirstate.parents())
+        affectswdir = (doomed is not None and 
+                       (parents[0] in doomed or parents[1] in doomed))
         transaction.rollback(self.sopener, self.sjoin("undo"),
                              self.ui.warn)
-        util.rename(self.join("undo.dirstate"), self.join("dirstate"))
-        if os.path.exists(self.join('undo.bookmarks')):
-            util.rename(self.join('undo.bookmarks'),
-                        self.join('bookmarks'))
-        try:
-            branch = self.opener.read("undo.branch")
-            self.dirstate.setbranch(branch)
-        except IOError:
-            self.ui.warn(_("named branch could not be reset, "
-                           "current branch is still: %s\n")
-                         % self.dirstate.branch())
+        if affectswdir:
+            util.rename(self.join('undo.dirstate'), self.join('dirstate'))
+            if os.path.exists(self.join('undo.bookmarks')):
+                util.rename(self.join('undo.bookmarks'),
+                            self.join('bookmarks'))
+            try:
+                branch = self.opener.read('undo.branch')
+                self.dirstate.setbranch(branch)
+            except IOError:
+                self.ui.warn(_('named branch could not be reset, '
+                               'current branch is still: %s\n')
+                             % self.dirstate.branch())
+
         self.invalidate()
         self.dirstate.invalidate()
         self.destroyed()
diff --git a/tests/test-rollback.t b/tests/test-rollback.t
--- a/tests/test-rollback.t
+++ b/tests/test-rollback.t
@@ -64,6 +64,37 @@
   $ hg branch
   test
 
+do not restore dirstate, branch when we don't need to
+  $ hg log --template '{rev}  {branch}  {desc|firstline}\n'
+  0  default  add a again
+  $ hg status
+  M a
+  $ hg commit -m'modify a again'
+  $ echo b > b
+  $ hg commit -Am'add b'
+  adding b
+  $ hg log --template '{rev}  {branch}  {desc|firstline}\n'
+  2  test  add b
+  1  test  modify a again
+  0  default  add a again
+  $ hg update default
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ cat .hg/undo.branch ; echo
+  test
+  $ hg rollback
+  repository tip rolled back to revision 1 (undo commit)
+  working directory now based on revision 0
+  $ hg id -n
+  0
+  $ hg branch
+  default
+
+# commented out because this bug is still there: I don't want anyone
+# to think that this is desirable behaviour!
+#oops: file b is irretrievably lost (issue 2998)
+#  $ hg status
+#  $ find . -name b -o -name 'b.[id]'
+
 rollback by pretxncommit saves commit message (issue 1635)
 
   $ echo a >> a
@@ -102,12 +133,12 @@
   adding changesets
   adding manifests
   adding file changes
-  added 2 changesets with 2 changes to 1 files
+  added 3 changesets with 2 changes to 1 files (+1 heads)
   updating to branch default
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cd u
   $ hg id default
-  8902593132ae
+  068774709090
 
 now rollback and observe that 'hg serve' reloads the repository and
 presents the correct tip changeset:


More information about the Mercurial-devel mailing list