# HG changeset patch # User Manuel Jacob # Date 1584819401 -3600 # Node ID f12d2172a1335c36d6656717fb8c84fc141ac0bb # Parent 9035901412e6827eb5a35a71f9eb40184d812c29 evolve: consider all obsolete ancestors when finding new parent for orphan Both the old and the new logic have in common that the orphan changeset is moved to the tipmost successor of one or more ancestors of the orphan changeset. The previous logic considered only the first non-pruned ancestor of the orphan changeset. The new logic considers all obsolete ancestors of the orphan changeset. The logic in _possibledestination() had to be changed to be consistent with the new logic. diff -r 9035901412e6 -r f12d2172a133 hgext3rd/evolve/evolvecmd.py --- a/hgext3rd/evolve/evolvecmd.py Wed Apr 29 00:44:14 2020 +0800 +++ b/hgext3rd/evolve/evolvecmd.py Sat Mar 21 20:36:41 2020 +0100 @@ -122,28 +122,37 @@ if not pctx.obsolete(): ui.warn(_(b"cannot solve instability of %s, skipping\n") % orig) return (False, b".") - obs = pctx - try: - newer = utility._singlesuccessor(repo, obs) - # search of a parent which isn't the orig - while repo[newer].node() == orig.node(): - obs = obs.parents()[0] - newer = utility._singlesuccessor(repo, obs) - except utility.MultipleSuccessorsError as exc: - if exc.divergenceflag: + targetancs = [] + # Find successors of the changesets that cause the orphan. These will be + # the ancestors of the target (unless they are split over multiple + # topological branches, in which case we ask the user to choose one). + for obsrev in repo.revs(b'::%d and obsolete()', pctx.rev()): + obs = repo[obsrev] + succs = obsutil.successorssets(repo, obs.node()) + if not succs: + continue + if len(succs) > 1: msg = _(b"skipping %s: divergent rewriting. can't choose " b"destination\n") % obs ui.write_err(msg) return (False, b".") - if exc.splitflag: - splitsucc = utility.picksplitsuccessor(ui, repo, obs, orig) - if not splitsucc[0]: + targetancs.extend(node for node in succs[0] if node != orig.node()) + if not targetancs: + # If no successors of the changesets that cause the orphan could be + # found, fall back to next non-obsolete ancestor. + newer = repo.revs(b'::%d and not obsolete()', pctx.rev()).last() + if newer is None: + newer = nodemod.nullid + else: + if True: # keep indentation for now + orphantarget = utility.pickorphantarget(ui, repo, targetancs, orig) + if not orphantarget[0]: msg = _(b"could not solve instability, " b"ambiguous destination: " - b"parent split across two branches\n") + b"ancestors split across different branches\n") ui.write_err(msg) return (False, b".") - newer = splitsucc[1] + newer = orphantarget[1] target = repo[newer] if not ui.quiet or confirm: repo.ui.write(_(b'move:'), label=b'evolve.operation') @@ -1203,10 +1212,15 @@ r = tovisit.pop() if r == -1: continue + ctx = repo[r] + if not (ctx.obsolete() or ctx.orphan()): + # None of the ancestors are obsolete, therefore we don't have to + # search them. The revision itself is a possible destination. + dest.add(r) + continue + tovisit.extend(parents(r)) succsets = obsutil.successorssets(repo, tonode(r), cache=cache) - if not succsets: - tovisit.extend(parents(r)) - else: + if succsets: # We should probably pick only one destination from split # (case where '1 < len(ss)'), This could be the currently tipmost # but logic is less clear when result of the split are now on @@ -1214,7 +1228,7 @@ for ss in succsets: for n in ss: dest.add(torev(n)) - return dest + return set(repo.revs(b'heads(%ld::%ld)', dest, dest)) def _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat): """Used by the evolve function to display an error message when diff -r 9035901412e6 -r f12d2172a133 hgext3rd/evolve/utility.py --- a/hgext3rd/evolve/utility.py Wed Apr 29 00:44:14 2020 +0800 +++ b/hgext3rd/evolve/utility.py Sat Mar 21 20:36:41 2020 +0100 @@ -126,24 +126,21 @@ return repo[newer[0][0]].rev() -def picksplitsuccessor(ui, repo, ctx, evolvecand): - """choose a successor of ctx from split targets +def pickorphantarget(ui, repo, targetancestors, evolvecand): + """choose a new target for an orphan changeset - Choose highest one if all successors are in a topological branch. And if - they are split over multiple topological branches, we ask user to choose - an evolve destination. + Choose highest one if all successors of its ancestors are in a topological + branch. And if they are on different topological branches, we ask the user + to choose an evolve destination. - Return (True, succ) unless split targets are split over multiple - topological branches and user didn't choose any evolve destination, - in which case return (False, '.') + Return (True, succ) unless successors of ancestors are on different + topological branches and user didn't choose any evolve destination, in + which case return (False, '.'). """ - targets = obsutil.successorssets(repo, ctx.node())[0] - assert targets - targetrevs = [repo[r].rev() for r in targets] - heads = repo.revs(b'heads(%ld::%ld)', targetrevs, targetrevs) + heads = repo.revs(b'heads(%ln::%ln)', targetancestors, targetancestors) if len(heads) > 1: - cheader = (_(b"ancestor of '%s' split over multiple topological" - b" branches.\nchoose an evolve destination:") % + cheader = (_(b"successors of ancestors of %s are on different " + b"topological branches\nchoose an evolve destination:") % evolvecand) selectedrev = revselectionprompt(ui, repo, list(heads), cheader) if selectedrev is None: diff -r 9035901412e6 -r f12d2172a133 tests/test-evolve-issue5832.t --- a/tests/test-evolve-issue5832.t Wed Apr 29 00:44:14 2020 +0800 +++ b/tests/test-evolve-issue5832.t Sat Mar 21 20:36:41 2020 +0100 @@ -107,7 +107,7 @@ move:[2] added b atop:[5] added a hg rebase -r a1da0651488c -d 7014ec2829cd - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches Resolving instability using `hg evolve` @@ -117,7 +117,7 @@ move:[2] added b atop:[5] added a move:[4] merge commit - ancestor of '7235ef625ea3' split over multiple topological branches. + successors of ancestors of 7235ef625ea3 are on different topological branches choose an evolve destination: 1: [62fb70414f99] added c 2: [5841d7cf9893] added d @@ -249,7 +249,7 @@ move:[2] added b atop:[6] added a hg rebase -r a1da0651488c -d 5568b87b1491 - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches $ hg evolve --any --all --config ui.interactive=True < 2 @@ -257,7 +257,7 @@ move:[2] added b atop:[6] added a move:[4] merge commit - ancestor of 'cdf2ea1b9312' split over multiple topological branches. + successors of ancestors of cdf2ea1b9312 are on different topological branches choose an evolve destination: 1: [62fb70414f99] added c 2: [5841d7cf9893] added d @@ -395,14 +395,14 @@ move:[2] added b atop:[6] added a hg rebase -r a1da0651488c -d 5568b87b1491 - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches $ hg evolve --any --all --config ui.interactive=True < 2 > EOF move:[2] added b atop:[6] added a - ancestor of 'b9b387427a53' split over multiple topological branches. + successors of ancestors of b9b387427a53 are on different topological branches choose an evolve destination: 1: [62fb70414f99] added c 2: [5841d7cf9893] added d diff -r 9035901412e6 -r f12d2172a133 tests/test-evolve-orphan-split.t --- a/tests/test-evolve-orphan-split.t Wed Apr 29 00:44:14 2020 +0800 +++ b/tests/test-evolve-orphan-split.t Sat Mar 21 20:36:41 2020 +0100 @@ -193,7 +193,7 @@ $ hg evolve --dry-run < 1 > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c @@ -206,7 +206,7 @@ $ hg evolve --dry-run < 2 > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c @@ -222,56 +222,56 @@ $ hg evolve --all < foo > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c q: quit the prompt enter the index of the revision you want to select: foo invalid value 'foo' entered for index - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches $ hg evolve --all < 4 > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c q: quit the prompt enter the index of the revision you want to select: 4 invalid value '4' entered for index - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches $ hg evolve --all < -1 > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c q: quit the prompt enter the index of the revision you want to select: -1 invalid value '-1' entered for index - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches $ hg evolve --all < q > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c q: quit the prompt enter the index of the revision you want to select: q - could not solve instability, ambiguous destination: parent split across two branches + could not solve instability, ambiguous destination: ancestors split across different branches Doing the evolve with the interactive prompt $ hg evolve --all < 1 > EOF - ancestor of 'd48a30875f01' split over multiple topological branches. + successors of ancestors of d48a30875f01 are on different topological branches choose an evolve destination: 1: [f2632392aefe] added a b c 2: [7f87764e5b64] added a b c diff -r 9035901412e6 -r f12d2172a133 tests/test-evolve-orphan-switched-ancestors.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-orphan-switched-ancestors.t Sat Mar 21 20:36:41 2020 +0100 @@ -0,0 +1,37 @@ + $ . $TESTDIR/testlib/common.sh + $ cat >> $HGRCPATH < [extensions] + > evolve= + > [alias] + > glog = log -GT "{rev}:{node|short} {desc}\n" + > EOF + +Test that, even if the order of the ancestors has been switched, the orphan is +rebased such that it has the sucessors of both original ancestors are the new +ancestors. + + $ hg init repo + $ cd repo + $ mkcommit a + $ mkcommit b + $ echo a1 > a; echo b1 > b; hg ci -m 'mod ab' + $ hg up null -q + $ hg pick 1 + picking 1:d2ae7f538514 "b" + 1 new orphan changesets + $ hg pick 0 + picking 0:cb9a9f314b8b "a" + $ hg next --dry-run + move:[2] mod ab + atop:[4] a + hg rebase -r cc6607605d1e -d f4a8092719b1 + $ hg evolve + move:[2] mod ab + atop:[4] a + $ hg glog + o 5:866738c73814 mod ab + | + @ 4:f4a8092719b1 a + | + o 3:6563da9dcf87 b +