changeset 10418:5fc090ba08a6

localrepo: add optional validation (defaults to off) for incoming changes This verifies that all manifests are present for incoming changes, and all files for those manifests are also present. This is a simple first-pass, and could be better, but seems like a valuable thing to have, as I've seen pushes in the past that propagated revlog corruption.
author Augie Fackler <durin42@gmail.com>
date Thu, 11 Feb 2010 16:37:43 -0600
parents 4cfd0d56be6d
children 992dc3d39357
files mercurial/localrepo.py tests/test-push-validation tests/test-push-validation.out
diffstat 3 files changed, 64 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/localrepo.py	Thu Feb 11 21:11:59 2010 +0100
+++ b/mercurial/localrepo.py	Thu Feb 11 16:37:43 2010 -0600
@@ -2005,6 +2005,16 @@
             # be empty during the pull
             self.manifest.addgroup(chunkiter, revmap, trp)
 
+            needfiles = {}
+            if self.ui.configbool('server', 'validate', default=False):
+                # validate incoming csets have their manifests
+                for cset in xrange(clstart, clend):
+                    mfest = self.changelog.read(self.changelog.node(cset))[0]
+                    mfest = self.manifest.readdelta(mfest)
+                    # store file nodes we must see
+                    for f, n in mfest.iteritems():
+                        needfiles.setdefault(f, set()).add(n)
+
             # process the files
             self.ui.status(_("adding file changes\n"))
             while 1:
@@ -2019,6 +2029,24 @@
                     raise util.Abort(_("received file revlog group is empty"))
                 revisions += len(fl) - o
                 files += 1
+                if f in needfiles:
+                    needs = needfiles[f]
+                    for new in xrange(o, len(fl)):
+                        n = fl.node(new)
+                        if n in needs:
+                            needs.remove(n)
+                    if not needs:
+                        del needfiles[f]
+
+            for f, needs in needfiles.iteritems():
+                fl = self.file(f)
+                for n in needs:
+                    try:
+                        fl.rev(n)
+                    except error.LookupError:
+                        raise util.Abort(
+                            _('missing file data for %s:%s - run hg verify') %
+                            (f, hex(n)))
 
             newheads = len(cl.heads())
             heads = ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-validation	Thu Feb 11 16:37:43 2010 -0600
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+STRIP=`pwd`
+
+hg init test
+cd test
+cat > .hg/hgrc <<EOF
+[server]
+validate=1
+EOF
+echo alpha > alpha
+echo beta > beta
+hg addr
+hg ci -m 1
+
+cd ..
+hg clone test test-clone
+
+cd test-clone
+cp .hg/store/data/beta.i tmp
+echo blah >> beta
+hg ci -m '2 (corrupt)'
+mv tmp .hg/store/data/beta.i
+hg push 2>&1 | sed "s%$STRIP%test-root%"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-validation.out	Thu Feb 11 16:37:43 2010 -0600
@@ -0,0 +1,12 @@
+adding alpha
+adding beta
+updating to branch default
+2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+pushing to test-root/test
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+transaction abort!
+rollback completed
+abort: missing file data for beta:dddc47b3ba30e54484720ce0f4f768a0f4b6efb9 - run hg verify