# HG changeset patch # User Pierre-Yves David # Date 1586132263 -7200 # Node ID 11c359b4071d32346b7b621733a4842bf1a4c3bc # Parent ba53591d4aab8c6265683446fc327209dfd4c12f head-checking: backport fix from 7d5455b988ec See 7d5455b988ec for details. Having a version in the evolve repository allow us to check the behavior when topic is enabled. It will also become the base of further head checking fix and topic compatibility. diff -r ba53591d4aab -r 11c359b4071d hgext3rd/evolve/__init__.py --- a/hgext3rd/evolve/__init__.py Mon Apr 06 04:01:58 2020 +0200 +++ b/hgext3rd/evolve/__init__.py Mon Apr 06 02:17:43 2020 +0200 @@ -292,6 +292,7 @@ state, evolvecmd, exthelper, + headchecking, metadata, obscache, obsexchange, @@ -346,6 +347,7 @@ eh.merge(compat.eh) eh.merge(cmdrewrite.eh) eh.merge(rewind.eh) +eh.merge(headchecking.eh) uisetup = eh.finaluisetup extsetup = eh.finalextsetup reposetup = eh.finalreposetup diff -r ba53591d4aab -r 11c359b4071d hgext3rd/evolve/headchecking.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext3rd/evolve/headchecking.py Mon Apr 06 02:17:43 2020 +0200 @@ -0,0 +1,113 @@ +from __future__ import absolute_import + +import functools + +from mercurial import ( + discovery, + extensions, + phases, +) + + +from . import ( + compat, + exthelper, +) + +eh = exthelper.exthelper() + + +@eh.uisetup +def uisetup(ui): + extensions.wrapfunction(discovery, '_postprocessobsolete', _postprocessobsolete) + +def branchinfo(pushop, repo, node): + return repo[node].branch() + +# taken from 7d5455b988ec + branchinfo abstraction. +def _postprocessobsolete(orig, pushop, futurecommon, candidate_newhs): + """post process the list of new heads with obsolescence information + + Exists as a sub-function to contain the complexity and allow extensions to + experiment with smarter logic. + + Returns (newheads, discarded_heads) tuple + """ + pushingmarkerfor = discovery.pushingmarkerfor + # known issue + # + # * We "silently" skip processing on all changeset unknown locally + # + # * if is public on the remote, it won't be affected by obsolete + # marker and a new is created + + # define various utilities and containers + repo = pushop.repo + unfi = repo.unfiltered() + torev = compat.getgetrev(unfi.changelog) + public = phases.public + getphase = unfi._phasecache.phase + ispublic = lambda r: getphase(unfi, r) == public + ispushed = lambda n: torev(n) in futurecommon + hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, ispushed) + successorsmarkers = unfi.obsstore.successors + newhs = set() # final set of new heads + discarded = set() # new head of fully replaced branch + + localcandidate = set() # candidate heads known locally + unknownheads = set() # candidate heads unknown locally + for h in candidate_newhs: + if h in unfi: + localcandidate.add(h) + else: + if successorsmarkers.get(h) is not None: + msg = ( + b'checkheads: remote head unknown locally has' + b' local marker: %s\n' + ) + repo.ui.debug(msg % hex(h)) + unknownheads.add(h) + + # fast path the simple case + if len(localcandidate) == 1: + return unknownheads | set(candidate_newhs), set() + + # actually process branch replacement + while localcandidate: + nh = localcandidate.pop() + current_branch = branchinfo(pushop, unfi, nh) + # run this check early to skip the evaluation of the whole branch + if torev(nh) in futurecommon or ispublic(torev(nh)): + newhs.add(nh) + continue + + # Get all revs/nodes on the branch exclusive to this head + # (already filtered heads are "ignored")) + branchrevs = unfi.revs( + b'only(%n, (%ln+%ln))', nh, localcandidate, newhs + ) + + branchnodes = [] + for r in branchrevs: + ctx = unfi[r] + if ctx.branch() == current_branch: + branchnodes.append(ctx.node()) + + # The branch won't be hidden on the remote if + # * any part of it is public, + # * any part of it is considered part of the result by previous logic, + # * if we have no markers to push to obsolete it. + if ( + any(ispublic(torev(n)) for n in branchnodes) + or any(torev(n) in futurecommon for n in branchnodes) + or any(not hasoutmarker(n) for n in branchnodes) + ): + newhs.add(nh) + else: + # note: there is a corner case if there is a merge in the branch. + # we might end up with -more- heads. However, these heads are not + # "added" by the push, but more by the "removal" on the remote so I + # think is a okay to ignore them, + discarded.add(nh) + newhs |= unknownheads + return newhs, discarded diff -r ba53591d4aab -r 11c359b4071d tests/test-push-checkheads-multibranches-E1.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-push-checkheads-multibranches-E1.t Mon Apr 06 02:17:43 2020 +0200 @@ -0,0 +1,93 @@ +==================================== +Testing head checking code: Case E-1 +==================================== + +Mercurial checks for the introduction of new heads on push. Evolution comes +into play to detect if existing branches on the server are being replaced by +some of the new one we push. + +This case is part of a series of tests checking this behavior. + +Category E: case involving changeset on multiple branch +TestCase 8: moving a branch to another location + +.. old-state: +.. +.. * 1-changeset on branch default +.. * 1-changeset on branch Z (above default) +.. +.. new-state: +.. +.. * 1-changeset on branch default +.. * 1-changeset on branch Z (rebased away from A0) +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. B ø⇠◔ B' +.. | | +.. A ◔ | +.. |/ +.. ● + + $ . $TESTDIR/testlib/topic_setup.sh + $ . $TESTDIR/testlib/push-checkheads-util.sh + +Test setup +---------- + + $ mkdir E1 + $ cd E1 + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg branch Z + marked working directory as branch Z + (branches are permanent and global, did you want a bookmark?) + $ mkcommit B0 + $ hg push --new-branch + pushing to $TESTTMP/E1/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + $ hg up 0 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg branch --force Z + marked working directory as branch Z + $ mkcommit B1 + created new head + (consider using topic for lightweight branches. See 'hg help topic') + $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"` + 1 new obsolescence markers + obsoleted 1 changesets + $ hg log -G --hidden + @ c98b855401e7 (draft): B1 + | + | x 93e5c1321ece (draft): B0 + | | + | o 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push + pushing to $TESTTMP/E1/server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 1 new obsolescence markers + obsoleted 1 changesets + + $ cd ../.. diff -r ba53591d4aab -r 11c359b4071d tests/test-push-checkheads-multibranches-E2.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-push-checkheads-multibranches-E2.t Mon Apr 06 02:17:43 2020 +0200 @@ -0,0 +1,108 @@ +==================================== +Testing head checking code: Case E-2 +==================================== + +Mercurial checks for the introduction of new heads on push. Evolution comes +into play to detect if existing branches on the server are being replaced by +some of the new one we push. + +This case is part of a series of tests checking this behavior. + +Category E: case involving changeset on multiple branch +TestCase 8: moving interleaved branch away from each other + +.. old-state: +.. +.. * 2-changeset on branch default +.. * 1-changeset on branch Z (between the two other) +.. +.. new-state: +.. +.. * 2-changeset on branch default, aligned +.. * 1-changeset on branch Z (at the same location) +.. +.. expected-result: +.. +.. * push allowed +.. +.. graph-summary: +.. +.. C ø⇠◔ C' +.. | | +.. B ◔ | +.. | | +.. A ø⇠◔ A' +.. |/ +.. ● + + $ . $TESTDIR/testlib/topic_setup.sh + $ . $TESTDIR/testlib/push-checkheads-util.sh + +Test setup +---------- + + $ mkdir E1 + $ cd E1 + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg branch Z + marked working directory as branch Z + (branches are permanent and global, did you want a bookmark?) + $ mkcommit B0 + $ hg branch default --force + marked working directory as branch default + $ mkcommit C0 + created new head + (consider using topic for lightweight branches. See 'hg help topic') + $ hg push --new-branch + pushing to $TESTTMP/E1/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + $ hg up 0 + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ mkcommit A1 + created new head + (consider using topic for lightweight branches. See 'hg help topic') + $ mkcommit C1 + $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"` + 1 new obsolescence markers + obsoleted 1 changesets + 2 new orphan changesets + $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"` + 1 new obsolescence markers + obsoleted 1 changesets + $ hg log -G --hidden + @ 0c76bc104656 (draft): C1 + | + o f6082bc4ffef (draft): A1 + | + | x afc55ba2ce61 (draft): C0 + | | + | * 93e5c1321ece (draft): B0 + | | + | x 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push -r 'desc("C1")' + pushing to $TESTTMP/E1/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + 2 new obsolescence markers + obsoleted 2 changesets + 1 new orphan changesets + + $ cd ../.. diff -r ba53591d4aab -r 11c359b4071d tests/test-push-checkheads-multibranches-E3.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-push-checkheads-multibranches-E3.t Mon Apr 06 02:17:43 2020 +0200 @@ -0,0 +1,97 @@ +==================================== +Testing head checking code: Case E-3 +==================================== + +Mercurial checks for the introduction of new heads on push. Evolution comes +into play to detect if existing branches on the server are being replaced by +some of the new one we push. + +This case is part of a series of tests checking this behavior. + +Category E: case involving changeset on multiple branch +TestCase 8: moving only part of the interleaved branch away, creating 2 heads + +.. old-state: +.. +.. * 2-changeset on branch default +.. * 1-changeset on branch Z (between the two other) +.. +.. new-state: +.. +.. * 2-changeset on branch default, on untouched, the other moved +.. * 1-changeset on branch Z (at the same location) +.. +.. expected-result: +.. +.. * push rejected +.. +.. graph-summary: +.. +.. C ø⇠◔ C' +.. | | +.. B ◔ | +.. | | +.. A ◔ | +.. |/ +.. ● + + $ . $TESTDIR/testlib/topic_setup.sh + $ . $TESTDIR/testlib/push-checkheads-util.sh + +Test setup +---------- + + $ mkdir E1 + $ cd E1 + $ setuprepos + creating basic server and client repo + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd client + $ hg branch Z + marked working directory as branch Z + (branches are permanent and global, did you want a bookmark?) + $ mkcommit B0 + $ hg branch default --force + marked working directory as branch default + $ mkcommit C0 + created new head + (consider using topic for lightweight branches. See 'hg help topic') + $ hg push --new-branch + pushing to $TESTTMP/E1/server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + $ hg up 0 + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ mkcommit C1 + created new head + (consider using topic for lightweight branches. See 'hg help topic') + $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"` + 1 new obsolescence markers + obsoleted 1 changesets + $ hg log -G --hidden + @ dc44c53142f0 (draft): C1 + | + | x afc55ba2ce61 (draft): C0 + | | + | o 93e5c1321ece (draft): B0 + | | + | o 8aaa48160adc (draft): A0 + |/ + o 1e4be0697311 (public): root + + +Actual testing +-------------- + + $ hg push -r 'desc("C1")' + pushing to $TESTTMP/E1/server + searching for changes + abort: push creates new remote head dc44c53142f0! + (merge or see 'hg help push' for details about pushing new heads) + [255] + + $ cd ../..