# HG changeset patch # User Matt Harbison # Date 2015-06-16 20:15:15 # Node ID f1d46075b13a90671c14b2737b1d4f07a3fa1a4a # Parent 183965a00c76d967597f209e4b9e387be9e8031c verify: check the subrepository references in .hgsubstate While hopefully atypical, there are reasons that a subrepository revision can be lost that aren't covered by corruption of the .hgsubstate revlog. Such things can happen when a subrepo is amended, stripped or simply isn't pulled from upstream because the parent repo revision wasn't updated yet. There's no way to know if it is an error, but this will find potential problems sooner than when some random revision is updated. Until recently, convert made no attempt at rewriting the .hgsubstate file. The impetuous for this is to verify the conversion of some repositories, and this is orders of magnitude faster than a bash script from 0..tip that does an 'hg update -C $rev'. But it is equally useful to determine if everything has been pulled down before taking a thumb drive on the go. It feels somewhat wrong to leave this out of verifymod (mostly because the file is already read in there, and the final summary is printed before the subrepos are checked). But verifymod looks very low level, so importing subrepo stuff there seems more wrong. diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -664,7 +664,28 @@ def revert(repo, node, choose): def verify(repo): """verify the consistency of a repository""" - return verifymod.verify(repo) + ret = verifymod.verify(repo) + + # Broken subrepo references in hidden csets don't seem worth worrying about, + # since they can't be pushed/pulled, and --hidden can be used if they are a + # concern. + + # pathto() is needed for -R case + revs = repo.revs("filelog(%s)", + util.pathto(repo.root, repo.getcwd(), '.hgsubstate')) + + if revs: + repo.ui.status(_('checking subrepo links\n')) + for rev in revs: + ctx = repo[rev] + try: + for subpath in ctx.substate: + ret = ctx.sub(subpath).verify() or ret + except Exception: + repo.ui.warn(_('.hgsubstate is corrupt in revision %s\n') % + node.short(ctx.node())) + + return ret def remoteui(src, opts): 'build a remote ui from ui or repo and opts' diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py --- a/mercurial/subrepo.py +++ b/mercurial/subrepo.py @@ -572,6 +572,12 @@ class abstractsubrepo(object): def shortid(self, revid): return revid + def verify(self): + '''verify the integrity of the repository. Return 0 on success or + warning, 1 on any error. + ''' + return 0 + @propertycache def wvfs(self): """return vfs to access the working directory of this subrepository @@ -1011,6 +1017,24 @@ class hgsubrepo(abstractsubrepo): def shortid(self, revid): return revid[:12] + def verify(self): + try: + rev = self._state[1] + ctx = self._repo.unfiltered()[rev] + if ctx.hidden(): + # Since hidden revisions aren't pushed/pulled, it seems worth an + # explicit warning. + ui = self._repo.ui + ui.warn(_("subrepo '%s' is hidden in revision %s\n") % + (self._relpath, node.short(self._ctx.node()))) + return 0 + except error.RepoLookupError: + # A missing subrepo revision may be a case of needing to pull it, so + # don't treat this as an error. + self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") % + (self._relpath, node.short(self._ctx.node()))) + return 0 + @propertycache def wvfs(self): """return own wvfs for efficiency and consitency diff --git a/tests/test-static-http.t b/tests/test-static-http.t --- a/tests/test-static-http.t +++ b/tests/test-static-http.t @@ -129,6 +129,7 @@ test with "/" URI (issue747) and subrepo crosschecking files in changesets and manifests checking files 3 files, 1 changesets, 3 total revisions + checking subrepo links $ cat a a $ hg paths diff --git a/tests/test-subrepo-missing.t b/tests/test-subrepo-missing.t --- a/tests/test-subrepo-missing.t +++ b/tests/test-subrepo-missing.t @@ -106,4 +106,19 @@ check that --hidden is propagated to the $ hg --hidden cat subrepo/a foo +verify will warn if locked-in subrepo revisions are hidden or missing + + $ hg ci -m "amended subrepo (again)" + $ hg --config extensions.strip= --hidden strip -R subrepo -qr 'tip' + $ hg verify + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 2 files, 5 changesets, 5 total revisions + checking subrepo links + subrepo 'subrepo' is hidden in revision a66de08943b6 + subrepo 'subrepo' is hidden in revision 674d05939c1e + subrepo 'subrepo' not found in revision a7d05d9055a4 + $ cd ..