[PATCH 2 of 2 STABLE] transplant: restore dirstate correctly at unexpected failure

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Wed Jul 29 16:24:59 CDT 2015


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1438204929 -32400
#      Thu Jul 30 06:22:09 2015 +0900
# Branch stable
# Node ID 99e88320d665e42d5da4d0090b764404d8151e15
# Parent  800e090e9c64a58d6ae312f2d8c2565a605fe322
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.

diff --git a/hgext/transplant.py b/hgext/transplant.py
--- a/hgext/transplant.py
+++ b/hgext/transplant.py
@@ -125,9 +125,10 @@ class transplanter(object):
         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 @@ class transplanter(object):
                             # 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 @@ class transplanter(object):
                         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 @@ class transplanter(object):
             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):
diff --git a/tests/test-transplant.t b/tests/test-transplant.t
--- a/tests/test-transplant.t
+++ b/tests/test-transplant.t
@@ -867,6 +867,7 @@ timestamp of them isn't changed on the f
   > [hooks]
   > fakedirstatewritetime = !
   > fakepatchtime = !
+  > [extensions]
   > abort = !
   > EOF
 
@@ -877,4 +878,41 @@ timestamp of them isn't changed on the f
   $ hg status -A r1
   M r1
 
-  $ cd ..
+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 ..


More information about the Mercurial-devel mailing list