transplant: restore dirstate correctly at unexpected failure stable
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Thu, 30 Jul 2015 06:22:09 +0900
branchstable
changeset 25879 99e88320d665
parent 25878 800e090e9c64
child 25880 6fb55c6c6562
transplant: restore dirstate correctly at unexpected failure Before this patch, transplant can't restore dirstate as expected at failure other than one while patching. This causes: - unexpected file status - dirstate refers already rollback-ed parent (only at failure of transplanting the 2nd or later revision) To restore dirstate correctly also at unexpected failure, this patch encloses scope of store lock and transaction by 'dirstateguard'. This is temporary fixing for stable branch. See DirstateTransactionPlan wiki page for detail about the future plan to treat dirstate consistently around scope boundary of transaction. https://mercurial.selenic.com/wiki/DirstateTransactionPlan This patch also adds 'if lock' examination for safety 'lock.release()', because creating 'dirstateguard' object may fail unexpectedly (e.g. IOError for saving dirstate). BTW, in the test script, putting section header '[extensions]' into '.hg/hgrc' is needed to fix incomplete disabling 'abort' extension at 4d1382fd96ff.
hgext/transplant.py
tests/test-transplant.t
--- a/hgext/transplant.py	Thu Jul 30 06:16:12 2015 +0900
+++ b/hgext/transplant.py	Thu Jul 30 06:22:09 2015 +0900
@@ -125,9 +125,10 @@
         diffopts = patch.difffeatureopts(self.ui, opts)
         diffopts.git = True
 
-        lock = wlock = tr = None
+        lock = wlock = tr = dsguard = None
         try:
             wlock = repo.wlock()
+            dsguard = cmdutil.dirstateguard(repo, 'transplant')
             lock = repo.lock()
             tr = repo.transaction('transplant')
             for rev in revs:
@@ -200,6 +201,7 @@
                             # Do not rollback, it is up to the user to
                             # fix the merge or cancel everything
                             tr.close()
+                            dsguard.close()
                             raise
                         if n and domerge:
                             self.ui.status(_('%s merged at %s\n') % (revstr,
@@ -212,6 +214,7 @@
                         if patchfile:
                             os.unlink(patchfile)
             tr.close()
+            dsguard.close()
             if pulls:
                 exchange.pull(repo, source.peer(), heads=pulls)
                 merge.update(repo, pulls[-1], False, False, None)
@@ -220,7 +223,10 @@
             self.transplants.write()
             if tr:
                 tr.release()
-            lock.release()
+            if lock:
+                lock.release()
+            if dsguard:
+                dsguard.release()
             wlock.release()
 
     def filter(self, filter, node, changelog, patchfile):
--- a/tests/test-transplant.t	Thu Jul 30 06:16:12 2015 +0900
+++ b/tests/test-transplant.t	Thu Jul 30 06:22:09 2015 +0900
@@ -867,6 +867,7 @@
   > [hooks]
   > fakedirstatewritetime = !
   > fakepatchtime = !
+  > [extensions]
   > abort = !
   > EOF
 
@@ -877,4 +878,41 @@
   $ hg status -A r1
   M r1
 
+Test that rollback by unexpected failure after transplanting the first
+revision restores dirstate correctly.
+
+  $ hg rollback -q
+  $ rm -f abort
+  $ hg update -q -C d11e3596cc1a
+  $ hg parents -T "{node|short}\n"
+  d11e3596cc1a
+  $ hg status -A
+  C r1
+  C r2
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > # emulate failure at transplanting the 2nd revision
+  > pretxncommit.abort = test ! -f abort
+  > EOF
+  $ hg transplant "22c515968f13::"
+  applying 22c515968f13
+  22c515968f13 transplanted to * (glob)
+  applying e38700ba9dd3
+  transaction abort!
+  rollback completed
+  abort: pretxncommit.abort hook exited with status 1
+  [255]
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > pretxncommit.abort = !
+  > EOF
+
+  $ hg parents -T "{node|short}\n"
+  d11e3596cc1a
+  $ hg status -A
+  M r1
+  ? abort
+  C r2
+
   $ cd ..