verify: check for orphaned dirlogs
authorMartin von Zweigbergk <martinvonz@google.com>
Wed, 03 Feb 2016 15:35:15 -0800
changeset 30316 962921c330b00d53accdb3d4075cfe8e6d2ce440
parent 30315 7297e9e13a8a5132ba1bb78c51cf8ab962048df8
child 30317 53f42c8d5f71c3f5caf11a00f827a47cea756645
push id187
push usergszorc@mozilla.com
push dateSun, 28 Feb 2016 01:45:48 +0000
verify: check for orphaned dirlogs We already report orphaned filelogs, i.e. revlogs for files that are not mentioned in any manifest. This change adds checking for orphaned dirlogs, i.e. revlogs that are not mentioned in any parent-directory dirlog. Note that, for fncachestore, only files mentioned in the fncache are considered, there's not check for files in .hg/store/meta that are not mentioned in the fncache. This is no different from the current situation for filelogs.
mercurial/verify.py
tests/test-treemanifest.t
--- a/mercurial/verify.py
+++ b/mercurial/verify.py
@@ -192,30 +192,32 @@ class verifier(object):
                     if _validpath(repo, f):
                         filelinkrevs.setdefault(_normpath(f), []).append(i)
             except Exception as inst:
                 self.refersmf = True
                 self.exc(i, _("unpacking changeset %s") % short(n), inst)
         ui.progress(_('checking'), None)
         return mflinkrevs, filelinkrevs
 
-    def _verifymanifest(self, mflinkrevs, dir=""):
+    def _verifymanifest(self, mflinkrevs, dir="", storefiles=None):
         repo = self.repo
         ui = self.ui
         mf = self.repo.manifest.dirlog(dir)
 
         if not dir:
             self.ui.status(_("checking manifests\n"))
 
         filenodes = {}
         subdirnodes = {}
         seen = {}
         label = "manifest"
         if dir:
             label = dir
+            revlogfiles = mf.files()
+            storefiles.difference_update(revlogfiles)
         if self.refersmf:
             # Do not check manifest if there are only changelog entries with
             # null manifests.
             self.checklog(mf, label, 0)
         total = len(mf)
         for i in mf:
             if not dir:
                 ui.progress(_('checking'), i, total=total, unit=_('manifests'))
@@ -255,21 +257,33 @@ class verifier(object):
                     self.err(c, _("parent-directory manifest refers to unknown "
                                   "revision %s") % short(m), label)
                 else:
                     self.err(c, _("changeset refers to unknown revision %s") %
                              short(m), label)
 
         if not dir and subdirnodes:
             self.ui.status(_("checking directory manifests\n"))
+            storefiles = set()
+            revlogv1 = self.revlogv1
+            for f, f2, size in repo.store.datafiles():
+                if not f:
+                    self.err(None, _("cannot decode filename '%s'") % f2)
+                elif (size > 0 or not revlogv1) and f.startswith('meta/'):
+                    storefiles.add(_normpath(f))
+
         for subdir, linkrevs in subdirnodes.iteritems():
-            subdirfilenodes = self._verifymanifest(linkrevs, subdir)
+            subdirfilenodes = self._verifymanifest(linkrevs, subdir, storefiles)
             for f, onefilenodes in subdirfilenodes.iteritems():
                 filenodes.setdefault(f, {}).update(onefilenodes)
 
+        if not dir and subdirnodes:
+            for f in sorted(storefiles):
+                self.warn(_("warning: orphan revlog '%s'") % f)
+
         return filenodes
 
     def _crosscheckfiles(self, filelinkrevs, filenodes):
         repo = self.repo
         ui = self.ui
         ui.status(_("crosschecking files in changesets and manifests\n"))
 
         total = len(filelinkrevs) + len(filenodes)
@@ -397,12 +411,12 @@ class verifier(object):
             # cross-check
             if f in filenodes:
                 fns = [(lr, n) for n, lr in filenodes[f].iteritems()]
                 for lr, node in sorted(fns):
                     self.err(lr, _("manifest refers to unknown revision %s") %
                              short(node), f)
         ui.progress(_('checking'), None)
 
-        for f in storefiles:
+        for f in sorted(storefiles):
             self.warn(_("warning: orphan revlog '%s'") % f)
 
         return len(files), revisions
--- a/tests/test-treemanifest.t
+++ b/tests/test-treemanifest.t
@@ -512,23 +512,30 @@ Verify reports missing dirlog
   $ hg verify
   checking changesets
   checking manifests
   checking directory manifests
    0: empty or missing b/
    b/@0: parent-directory manifest refers to unknown revision 67688a370455
    b/@1: parent-directory manifest refers to unknown revision f38e85d334c5
    b/@2: parent-directory manifest refers to unknown revision 99c9792fd4b0
+  warning: orphan revlog 'meta/b/bar/00manifest.i'
+  warning: orphan revlog 'meta/b/bar/orange/00manifest.i'
+  warning: orphan revlog 'meta/b/bar/orange/fly/00manifest.i'
+  warning: orphan revlog 'meta/b/foo/00manifest.i'
+  warning: orphan revlog 'meta/b/foo/apple/00manifest.i'
+  warning: orphan revlog 'meta/b/foo/apple/bees/00manifest.i'
   crosschecking files in changesets and manifests
    b/bar/fruits.txt@0: in changeset but not in manifest
    b/bar/orange/fly/gnat.py@0: in changeset but not in manifest
    b/bar/orange/fly/housefly.txt@0: in changeset but not in manifest
    b/foo/apple/bees/flower.py@0: in changeset but not in manifest
   checking files
   8 files, 3 changesets, 10 total revisions
+  6 warnings encountered!
   8 integrity errors encountered!
   (first damaged changeset appears to be 0)
   [1]
   $ cp -rT .hg/store-newcopy .hg/store
 
 Verify reports missing dirlog entry
   $ mv -f .hg/store-copy/meta/b/00manifest.* .hg/store/meta/b/
   $ hg verify