Mercurial > public > mercurial-scm > hg
comparison mercurial/exchange.py @ 43913:4b7d5d10c45d
exchange: ensure all outgoing subrepo references are present before pushing
We've run into occasional problems with people committing a repo, and then
amending or rebasing in the subrepo. That makes it so that the revision in the
parent can't be checked out, and the problem gets propagated on push. Mercurial
already tries to defend against this sort of dangling reference by pushing *all*
subrepo revisions first. This reuses the checks that trigger warnings in
`hg verify` to bail on the push unless using `--force`.
I thought about putting this on the server side, but at that point, all of the
data has been transferred, only to bail out. Additionally, SCM Manager hosts
subrepos in a location that isn't nested in the parent, so normal subrepo code
would complain that the subrepo is missing when run on the server.
Because the push command pushes subrepos before calling this exchange code, a
subrepo will be pushed before the parent is verified. Not great, but no
dangling references are exchanged, so it solves the problem. This code isn't in
the loop that pushes the subrepos because:
1) the list of outgoing revisions is needed to limit the scope of the check
2) the loop only accesses the current revision, and therefore can miss
subrepos that were dropped in previous commits
3) this code is called when pushing a subrepo, so the protection is recursive
I'm not sure if there's a cheap check for the list of files in the outgoing
bundle. If there is, that would provide a fast path to bypass this check for
people not using subrepos (or if no subrepo changes were made). There's
probably also room for verifying other references like tags. But since that
doesn't break checkouts, it's much less of a problem.
Differential Revision: https://phab.mercurial-scm.org/D7616
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Thu, 12 Dec 2019 12:30:15 -0500 |
parents | ea97cd64c500 |
children | a61287a95dc3 |
comparison
equal
deleted
inserted
replaced
43912:a89381e04c58 | 43913:4b7d5d10c45d |
---|---|
644 with lock or util.nullcontextmanager(): | 644 with lock or util.nullcontextmanager(): |
645 with pushop.trmanager or util.nullcontextmanager(): | 645 with pushop.trmanager or util.nullcontextmanager(): |
646 pushop.repo.checkpush(pushop) | 646 pushop.repo.checkpush(pushop) |
647 _checkpublish(pushop) | 647 _checkpublish(pushop) |
648 _pushdiscovery(pushop) | 648 _pushdiscovery(pushop) |
649 if not pushop.force: | |
650 _checksubrepostate(pushop) | |
649 if not _forcebundle1(pushop): | 651 if not _forcebundle1(pushop): |
650 _pushbundle2(pushop) | 652 _pushbundle2(pushop) |
651 _pushchangeset(pushop) | 653 _pushchangeset(pushop) |
652 _pushsyncphase(pushop) | 654 _pushsyncphase(pushop) |
653 _pushobsolete(pushop) | 655 _pushobsolete(pushop) |
690 def _pushdiscovery(pushop): | 692 def _pushdiscovery(pushop): |
691 """Run all discovery steps""" | 693 """Run all discovery steps""" |
692 for stepname in pushdiscoveryorder: | 694 for stepname in pushdiscoveryorder: |
693 step = pushdiscoverymapping[stepname] | 695 step = pushdiscoverymapping[stepname] |
694 step(pushop) | 696 step(pushop) |
697 | |
698 | |
699 def _checksubrepostate(pushop): | |
700 """Ensure all outgoing referenced subrepo revisions are present locally""" | |
701 for n in pushop.outgoing.missing: | |
702 ctx = pushop.repo[n] | |
703 | |
704 if b'.hgsub' in ctx.manifest() and b'.hgsubstate' in ctx.files(): | |
705 for subpath in sorted(ctx.substate): | |
706 sub = ctx.sub(subpath) | |
707 sub.verify(onpush=True) | |
695 | 708 |
696 | 709 |
697 @pushdiscovery(b'changeset') | 710 @pushdiscovery(b'changeset') |
698 def _pushdiscoverychangeset(pushop): | 711 def _pushdiscoverychangeset(pushop): |
699 """discover the changeset that need to be pushed""" | 712 """discover the changeset that need to be pushed""" |