On push, Mercurial ensures that no obsolete and unstable changesets are included. However, when the obsolete or unstable changeset is already on the server, Mercurial still complains, unless the set of outgoing changesets is empty. $ cat >> $HGRCPATH <<EOF > [phases] > publish = False > [experimental] > evolution = true > EOF $ hg init server $ hg clone server client -q $ cd client $ echo root > root; hg add root; hg ci -m root $ hg branch foo -q $ echo A > A; hg add A; hg ci -m A0 $ echo B > B; hg add B; hg ci -m B0 $ hg push pushing to $TESTTMP/server searching for changes adding changesets adding manifests adding file changes added 3 changesets with 3 changes to 3 files $ hg up 1 -q $ hg ci -m A1 --amend 1 new orphan changesets $ hg log -G -T '{rev}:{node|short} "{desc}"' @ 3:8af15adf44a9 "A1" | | * 2:15102ccfca43 "B0" | | | x 1:a741f487acb6 "A0" |/ o 0:1e4be0697311 "root" FIXME: Push aborts even though 15102ccfca43 (B0) is already on the server. $ hg push pushing to $TESTTMP/server searching for changes abort: push includes orphan changeset: 15102ccfca43! [255] $ hg push --force pushing to $TESTTMP/server searching for changes adding changesets adding manifests adding file changes added 1 changesets with 0 changes to 0 files (+1 heads) 1 new obsolescence markers obsoleted 1 changesets 1 new orphan changesets If there are no outgoing changes, it works. $ hg push pushing to $TESTTMP/server searching for changes no changes found [1] Create unrelated changeset C0. $ hg up 0 -q $ echo C > C; hg add C; hg ci -m C0 $ hg log -G -T '{rev}:{node|short} "{desc}"' @ 4:a26de091fce5 "C0" | | o 3:8af15adf44a9 "A1" |/ | * 2:15102ccfca43 "B0" | | | x 1:a741f487acb6 "A0" |/ o 0:1e4be0697311 "root" FIXME: Push aborts even though 15102ccfca43 (B0) is already on the server. $ hg push pushing to $TESTTMP/server searching for changes abort: push includes orphan changeset: 15102ccfca43! [255] Explicitly pushing C0 works, even though $ hg out -G -T '{rev}:{node|short} "{desc}"' $ hg push -r 4 pushing to $TESTTMP/server searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) As above, if there are no outgoing changes, it works. $ hg push pushing to $TESTTMP/server searching for changes no changes found [1]
Created attachment 2078 [details] Code to demonstrate how various `outgoing` members don't do what is documented.
The code for checking outgoing changesets is in mercurial.exchange._pushcheckoutgoing(). The problem is the name and the usage of the `outgoing.missingheads` attribute. The name suggests that it contains the heads of the missing changesets, and that’s also what the docstring in the `outgoing` class says. However, `outgoing.missingheads` may contain changesets that are already on the server (causing this bug) or non-heads. What it actually contains is which revisions were passed to the push via `-r`, or `repo.heads()` by default, minus secret changesets. Therefore, `outgoing.missingheads` is not helpful for what we want to do here. (The attached patch shows how the `outgoing` members don’t conform to the documentation. A separate patch will be sent for fixing the name and documentation of `outgoing.missingheads`.) The `outgoing.missing` attribute doesn’t have this problem. As the buggy logic is shortcut if `outgoing.missing` is empty, the bug doesn’t happen if there are no outgoing changes. I see two ways to fix the actual problem: 1) Use `outgoing.missing`. This would have the advantage that all included obsolete and unstable changesets are shown. 2) Compute the heads of `outgoing.missing` and use them instead of `outgoing.missingheads`, and leave the code otherwise unchanged. This would mean that, like now, only the first encountered obsolete or unstable changeset is shown.
Solution 2) seems like a good fit.
I ended up implementing solution (1), as I think it’s more helpful for the user.
Fixed by https://mercurial-scm.org/repo/hg/rev/5631b0116374 Manuel Jacob <me@manueljacob.de> discovery: fix docstring of `outgoing` class Also, introduce a more correct name `ancestorsof` for what was named `missingheads` before. For now, we just forward `ancestorsof` to `missingheads` until all users are changed. There were some mistakes in the old docstring / name: * `missingheads` (new name: `ancestorsof`) contains the revs whose ancestors are included in the outgoing operation. It may contain non-head revs and revs which are already on the remote, so the name "missingheads" is wrong in two ways. * `missing` contains only ancestors of `missingheads`, so not *all nodes* present in local but not in remote. * `common` might not contain all common revs, e.g. not some that are not an ancestor of `missingheads`. It seems like the misleading name have fostered an actual bug (issue6372), where `outgoing.missingheads` was used assuming that it contains the heads of the missing changesets. (please test the fix)
The actual fix is in https://www.mercurial-scm.org/repo/hg-all/rev/c26335fa4225
Fixed by https://mercurial-scm.org/repo/hg/rev/a381618210d0 Manuel Jacob <me@manueljacob.de> tests: test that push doesn’t complain about unstable changesets if no changes When there’re no outgoing changes, push doesn’t complain about unstable changesets. There is currently a bug (see issue6372) that causes that there is an abort on push when the outgoing changes contain another changeset even if that is not obsolete or unstable. A test case and fix for that is sent in the next patch. (please test the fix)
Bug was set to TESTING for 7 days, resolving
The fix was backed out in 6063c1857d0a.
Bug was inactive for 150 days, archiving
Here’s another test case. It shows a case which the current check misses and a case which the current check rejects for no good reason. $ . $TESTDIR/testlib/push-checkheads-util.sh $ mkdir test $ cd test $ 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 $ mkcommit B0 $ hg log -G --hidden @ d73caddc5533 (draft): B0 | o 8aaa48160adc (draft): A0 | o 1e4be0697311 (public): root $ hg push pushing to $TESTTMP/test/server searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files Obsolete A0, making B0 orphan. $ hg debugobsolete `getid "desc(A0)" ` 1 new obsolescence markers obsoleted 1 changesets 1 new orphan changesets $ hg log -G --hidden @ d73caddc5533 (draft): B0 | x 8aaa48160adc (draft): A0 | o 1e4be0697311 (public): root Pushing makes B0 orphan on the server, but it’s not prevented. $ hg push pushing to $TESTTMP/test/server searching for changes no changes found 1 new obsolescence markers obsoleted 1 changesets 1 new orphan changesets [1] Add an unrelated changeset. $ hg up 0 -q $ hg branch unrelated -q $ mkcommit C0 $ hg log -G --hidden @ d4abdb58a1dc (draft): C0 | | * d73caddc5533 (draft): B0 | | | x 8aaa48160adc (draft): A0 |/ o 1e4be0697311 (public): root Pushing fails for no good reason (B0 is already orphan on the server). $ hg push --new-branch pushing to $TESTTMP/test/server searching for changes abort: push includes orphan changeset: d73caddc5533! [255]
Bug was inactive for 154 days, archiving