[PATCH 1 of 3 STABLE] transactions: fix hg recover with fncache backups

Durham Goode durham at fb.com
Tue Oct 21 14:58:34 CDT 2014


# HG changeset patch
# User Durham Goode <durham at fb.com>
# Date 1413849236 25200
#      Mon Oct 20 16:53:56 2014 -0700
# Branch stable
# Node ID e3cf0a29709409a7de70205d416325e0240d283c
# Parent  c1ae0b2c1719f56b906472efea8b20ca0774c968
transactions: fix hg recover with fncache backups

The transaction backupfiles logic was broken for 'hg recover'.  The file format
is XXX\0XXX\0YYY\0YYY\0 but the parser did a couple things wrong. 1) It went one
step beyond the final \0 and tried to read past the end of the array. 2)
array[i:i+1] returns a single item, instead of two items as intended.

Added a test to catch it, which turns out to be the first actual 'hg recover'
test.

diff --git a/mercurial/transaction.py b/mercurial/transaction.py
--- a/mercurial/transaction.py
+++ b/mercurial/transaction.py
@@ -349,8 +349,9 @@ def rollback(opener, file, report):
         data = fp.read()
         if len(data) > 0:
             parts = data.split('\0')
-            for i in xrange(0, len(parts), 2):
-                f, b = parts[i:i + 1]
+            # Skip the final part, since it's just a trailing empty space
+            for i in xrange(0, len(parts) - 1, 2):
+                f, b = parts[i:i + 2]
                 backupentries.append((f, b, None))
 
     _playback(file, report, opener, entries, backupentries)
diff --git a/tests/test-fncache.t b/tests/test-fncache.t
--- a/tests/test-fncache.t
+++ b/tests/test-fncache.t
@@ -236,3 +236,46 @@ Aborting transaction prevents fncache ch
   [255]
   $ cat .hg/store/fncache
   data/y.i
+
+Aborted transactions can be recovered later
+
+  $ cat > ../exceptionext.py <<EOF
+  > import os
+  > from mercurial import commands, util, transaction
+  > from mercurial.extensions import wrapfunction
+  > 
+  > def closewrapper(orig, self, *args, **kwargs):
+  >     origonclose = self.onclose
+  >     def onclose():
+  >         origonclose()
+  >         raise util.Abort("forced transaction failure")
+  >     self.onclose = onclose
+  >     return orig(self, *args, **kwargs)
+  > 
+  > def abortwrapper(orig, self, *args, **kwargs):
+  >     raise util.Abort("forced transaction failure")
+  > 
+  > def uisetup(ui):
+  >     wrapfunction(transaction.transaction, 'close', closewrapper)
+  >     wrapfunction(transaction.transaction, '_abort', abortwrapper)
+  > 
+  > cmdtable = {}
+  > 
+  > EOF
+  $ rm -f "${extpath}c"
+  $ hg up -q 1
+  $ touch z
+  $ hg ci -qAm z 2>/dev/null
+  [255]
+  $ cat .hg/store/fncache | sort
+  data/y.i
+  data/z.i
+  $ hg recover
+  rolling back interrupted transaction
+  checking changesets
+  checking manifests
+  crosschecking files in changesets and manifests
+  checking files
+  1 files, 1 changesets, 1 total revisions
+  $ cat .hg/store/fncache
+  data/y.i


More information about the Mercurial-devel mailing list