[PATCH 15 of 15 RFC] localrepo: invoke partial verification just after transaction commit

FUJIWARA Katsunori foozy at lares.dti.ne.jp
Wed Oct 3 11:39:12 CDT 2012


# HG changeset patch
# User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
# Date 1349281445 -32400
# Node ID 48b5053a6579bf5e95dca574d7c1290b93007b91
# Parent  bd4beabe19aa68a70c545c7a3b77e42457df10e4
localrepo: invoke partial verification just after transaction commit

This partial verification reads in revlogs changed by specific
revisions from filesystem again, so it can detect repository
corruption by some of external causes: for example, unreliable
filesystem (network sharing and so on) or protection by rude
anti-virus software.

diff -r bd4beabe19aa -r 48b5053a6579 mercurial/localrepo.py
--- a/mercurial/localrepo.py	Thu Oct 04 01:24:05 2012 +0900
+++ b/mercurial/localrepo.py	Thu Oct 04 01:24:05 2012 +0900
@@ -13,6 +13,7 @@
 import match as matchmod
 import merge as mergemod
 import tags as tagsmod
+import verify as verifymod
 from lock import release
 import weakref, errno, os, time, inspect
 propertycache = util.propertycache
@@ -1391,6 +1392,24 @@
         self._afterlock(commithook)
         return ret
 
+    def _verifytxn(self, clrevs, mfrevs):
+        if not self.ui.configbool('ui', 'verifytxn', True):
+            return
+        # create another instance to re-read revlogs in from filesystem
+        target = localrepository(self.baseui, self.root)
+        if not self.ui.configbool('ui', 'verifytxn_verbose', False):
+            # verify quietly
+            target.ui.quiet = True
+            target.ui.verbose = False
+            target.ui.debugflag = False
+        if not verifymod.verify(target, (clrevs, mfrevs, self.lock)):
+            return
+        msg = _('repository data is corrupted, even though'
+                ' transaction was committed successfully')
+        hint = _('see "hg help verify" for more information about'
+                 ' recovery from repository corruption')
+        raise util.Abort(msg, hint=hint)
+
     def commitctx(self, ctx, error=False):
         """Add a new revision to current repository.
         Revision information is passed via the context argument.
@@ -1400,12 +1419,16 @@
         removed = list(ctx.removed())
         p1, p2 = ctx.p1(), ctx.p2()
         user = ctx.user()
+        changesets = 0
 
         lock = self.lock()
         try:
             tr = self.transaction("commit")
             trp = weakref.proxy(tr)
 
+            clstart = clend = len(self.changelog)
+            mfstart = len(self.manifest)
+
             if ctx.files():
                 m1 = p1.manifest().copy()
                 m2 = p2.manifest()
@@ -1466,10 +1489,15 @@
                 phases.retractboundary(self, targetphase, [n])
             tr.close()
             self.updatebranchcache()
+            clend = len(self.changelog)
+            changesets = clend - clstart
             return n
         finally:
             if tr:
                 tr.release()
+            if changesets:
+                self._verifytxn(range(clstart, clend),
+                                range(mfstart, len(self.manifest)))
             lock.release()
 
     def destroyed(self, newheadnodes=None):
@@ -2304,6 +2332,7 @@
             # pull off the changeset group
             self.ui.status(_("adding changesets\n"))
             clstart = len(cl)
+            mfstart = len(self.manifest)
             class prog(object):
                 step = _('changesets')
                 count = 1
@@ -2452,6 +2481,9 @@
 
         finally:
             tr.release()
+        if changesets:
+            self._verifytxn(range(clstart, clend),
+                            range(mfstart, len(self.manifest)))
         # never return 0 here:
         if dh < 0:
             return dh - 1


More information about the Mercurial-devel mailing list