diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -646,6 +646,8 @@ pushop.repo.checkpush(pushop) _checkpublish(pushop) _pushdiscovery(pushop) + if not pushop.force: + _checksubrepostate(pushop) if not _forcebundle1(pushop): _pushbundle2(pushop) _pushchangeset(pushop) @@ -694,6 +696,17 @@ step(pushop) +def _checksubrepostate(pushop): + """Ensure all outgoing referenced subrepo revisions are present locally""" + for n in pushop.outgoing.missing: + ctx = pushop.repo[n] + + if b'.hgsub' in ctx.manifest() and b'.hgsubstate' in ctx.files(): + for subpath in sorted(ctx.substate): + sub = ctx.sub(subpath) + sub.verify(onpush=True) + + @pushdiscovery(b'changeset') def _pushdiscoverychangeset(pushop): """discover the changeset that need to be pushed""" diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py --- a/mercurial/subrepo.py +++ b/mercurial/subrepo.py @@ -429,10 +429,12 @@ convert this repository from shared to normal storage. ''' - def verify(self): - '''verify the integrity of the repository. Return 0 on success or - warning, 1 on any error. - ''' + def verify(self, onpush=False): + """verify the revision of this repository that is held in `_state` is + present and not hidden. Return 0 on success or warning, 1 on any + error. In the case of ``onpush``, warnings or errors will raise an + exception if the result of pushing would be a broken remote repository. + """ return 0 @propertycache @@ -1013,26 +1015,35 @@ hg.unshare(self.ui, self._repo) - def verify(self): + def verify(self, onpush=False): 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( - _(b"subrepo '%s' is hidden in revision %s\n") - % (self._relpath, node.short(self._ctx.node())) + msg = _(b"subrepo '%s' is hidden in revision %s") % ( + self._relpath, + node.short(self._ctx.node()), ) + + if onpush: + raise error.Abort(msg) + else: + self._repo.ui.warn(b'%s\n' % msg) 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( - _(b"subrepo '%s' not found in revision %s\n") - % (self._relpath, node.short(self._ctx.node())) + # don't treat this as an error for `hg verify`. + msg = _(b"subrepo '%s' not found in revision %s") % ( + self._relpath, + node.short(self._ctx.node()), ) + + if onpush: + raise error.Abort(msg) + else: + self._repo.ui.warn(b'%s\n' % msg) return 0 @propertycache diff --git a/tests/test-amend-subrepo.t b/tests/test-amend-subrepo.t --- a/tests/test-amend-subrepo.t +++ b/tests/test-amend-subrepo.t @@ -164,4 +164,35 @@ R .hgsub R .hgsubstate +broken repositories will refuse to push + +#if obsstore-off + $ hg up -q -C 2 +#else + $ hg up -q -C 6 +#endif + $ echo c >> t/b + $ hg amend -q -R t + + $ hg init ../dest + $ hg init ../dest/t + $ hg init ../dest/s + $ hg push -q ../dest + abort: subrepo 't' is hidden in revision 04aa62396ec6 (obsstore-on !) + abort: subrepo 't' not found in revision 04aa62396ec6 (obsstore-off !) + [255] + +... unless forced + + $ hg push --force -q ../dest + $ hg verify -R ../dest + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + checked 5 changesets with 12 changes to 4 files + checking subrepo links + subrepo 't' not found in revision 04aa62396ec6 + subrepo 't' not found in revision 6bce99600681 + $ cd ..