# HG changeset patch # User Pierre-Yves David # Date 2020-03-06 22:27:28 # Node ID 7d5455b988ece5dc4749e8184950aaa08fc68268 # Parent dca4b5da417d8d8afac527ebc588e5c9fff4befa discovery: avoid wrong detection of multiple branch heads (issue6256) This fix the code using obsolescence markers to remove "to be obsoleted" heads during the detection of new head creation from push. The code turned out to not use the branch information at all. This lead changeset from different branch to be detected as new head on unrelated branch. The code fix is actually quite simple. New tests have been added to covers these cases. Differential Revision: https://phab.mercurial-scm.org/D8259 diff --git a/mercurial/discovery.py b/mercurial/discovery.py --- a/mercurial/discovery.py +++ b/mercurial/discovery.py @@ -498,7 +498,6 @@ def _postprocessobsolete(pushop, futurec # define various utilities and containers repo = pushop.repo unfi = repo.unfiltered() - tonode = unfi.changelog.node torev = unfi.changelog.index.get_rev public = phases.public getphase = unfi._phasecache.phase @@ -530,6 +529,7 @@ def _postprocessobsolete(pushop, futurec # actually process branch replacement while localcandidate: nh = localcandidate.pop() + current_branch = unfi[nh].branch() # run this check early to skip the evaluation of the whole branch if torev(nh) in futurecommon or ispublic(torev(nh)): newhs.add(nh) @@ -540,7 +540,12 @@ def _postprocessobsolete(pushop, futurec branchrevs = unfi.revs( b'only(%n, (%ln+%ln))', nh, localcandidate, newhs ) - branchnodes = [tonode(r) for r in branchrevs] + + branchnodes = [] + for r in branchrevs: + c = unfi[r] + if c.branch() == current_branch: + branchnodes.append(c.node()) # The branch won't be hidden on the remote if # * any part of it is public, diff --git a/tests/test-push-checkheads-multibranches-E1.t b/tests/test-push-checkheads-multibranches-E1.t new file mode 100644 --- /dev/null +++ b/tests/test-push-checkheads-multibranches-E1.t @@ -0,0 +1,91 @@ +==================================== +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/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 + $ 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 --git a/tests/test-push-checkheads-multibranches-E2.t b/tests/test-push-checkheads-multibranches-E2.t new file mode 100644 --- /dev/null +++ b/tests/test-push-checkheads-multibranches-E2.t @@ -0,0 +1,105 @@ +==================================== +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/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 + $ 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 + $ 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 --git a/tests/test-push-checkheads-multibranches-E3.t b/tests/test-push-checkheads-multibranches-E3.t new file mode 100644 --- /dev/null +++ b/tests/test-push-checkheads-multibranches-E3.t @@ -0,0 +1,94 @@ +==================================== +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/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 + $ 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 + $ 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 ../..