diff --git a/hgext/amend.py b/hgext/amend.py --- a/hgext/amend.py +++ b/hgext/amend.py @@ -165,6 +165,7 @@ target_ctx = scmutil.revsingle(repo, opts['rev']) to_rewrite = repo.revs(b'%d::.', target_ctx.rev()) rewriteutil.precheck(repo, to_rewrite, b'amend') + descendants = repo.set(b'%ld - %d', to_rewrite, target_ctx.rev()) try: # Create a temporary commit of the working copy. @@ -180,6 +181,7 @@ temp_ctx = repo[b'tip'] state[b'target_node'] = target_ctx.node() state[b'temp_node'] = temp_ctx.node() + state[b'descendant_nodes'] = [ctx.node() for ctx in descendants] _do_continue_amend_rev(ui, repo, state, rebase) except error.InterventionRequired: raise @@ -219,7 +221,10 @@ rebased_temp_ctx = _rebase_temp_node( ui, repo, state, rebase, temp_ctx, target_ctx ) - _fold_temp_node(ui, repo, state, rebase, rebased_temp_ctx, target_ctx) + amended_ctx = _fold_temp_node( + ui, repo, state, rebase, rebased_temp_ctx, target_ctx + ) + _rebase_descendants(ui, repo, state, rebase, amended_ctx) def _rebase_temp_node(ui, repo, state, rebase, temp_ctx, target_ctx): @@ -282,6 +287,34 @@ return amended_ctx +def _rebase_descendants(ui, repo, state, rebase, amended_ctx): + if b'rebased_descendant_nodes' in state: + return + elif statemod.ischildunfinished(repo, b'amend', b'rebase'): + with statemod.delegating(repo, b'amend', b'rebase'): + ret = statemod.continuechild(ui, repo, b'amend', b'rebase') + else: + descendant_nodes = state[b'descendant_nodes'] + if descendant_nodes: + with statemod.delegating(repo, b'amend', b'rebase'): + ret = rebase.rebase( + ui, + repo, + rev=[revsetlang.formatspec(b'%ln', descendant_nodes)], + dest=revsetlang.formatspec(b'%d', amended_ctx.rev()), + ) + else: + ret = 0 + + if ret: + raise error.Abort(_(b'failed to rebase descendants of target')) + + rebased_descendants = repo.set(b'only(tip, %d)', amended_ctx.rev()) + state[b'rebased_descendant_nodes'] = [ + ctx.node() for ctx in rebased_descendants + ] + + def _abort_amend_rev(ui, repo): """Aborts the operation based on the state on disk.""" with repo.wlock(), repo.lock(), repo.transaction(b'amend'): @@ -308,6 +341,9 @@ rebased_temp_node = state.get(b'rebased_temp_node') to_strip.append(rebased_temp_node) to_strip.append(state.get(b'amended_node')) + rebased_descendant_nodes = state.get(b'rebased_descendant_nodes') + if rebased_descendant_nodes: + to_strip.extend(rebased_descendant_nodes) to_strip = [node for node in to_strip if node and node in unfi] if to_strip: repair.delayedstrip(ui, unfi, to_strip) diff --git a/tests/test-amend-rev.t b/tests/test-amend-rev.t --- a/tests/test-amend-rev.t +++ b/tests/test-amend-rev.t @@ -52,14 +52,12 @@ Can amend into grandparent $ hg amend -r 'desc("modify a")' - 1 new orphan changesets + rebasing 2:42e29cb5ca48 "add b" $ hg log -G -T '{rev} {desc}' + @ 6 add b + | o 5 modify a | - | @ 2 add b - | | (known-bad-output !) - | x 1 modify a (known-bad-output !) - |/ (known-bad-output !) o 0 add a Target commit has new content @@ -148,6 +146,75 @@ $ hg st -v +Descendants of the working copy parent don't get rebased +-------------------------------------------------------------------------------- + +Set up a repo with a few commits + + $ cd $TESTTMP + $ hg init side-branches + $ cd side-branches + $ echo a > a + $ hg ci -Aqm 'add a' + $ echo a2 > a + $ hg ci -Aqm 'modify a' + $ echo a3 > a + $ hg ci -Aqm 'change a' + $ hg co '.^^' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo b > b + $ hg ci -Aqm 'add b' + $ hg merge 'desc("modify a")' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m merge + $ echo b2 > b + $ hg ci -Aqm 'modify b' + $ hg co '.^' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -G -T '{rev} {desc}' + o 5 modify b + | + @ 4 merge + |\ + | o 3 add b + | | + +---o 2 change a + | | + o | 1 modify a + |/ + o 0 add a + + + $ echo a4 > a + $ hg amend -r 'desc("modify a")' --config experimental.evolution.allowunstable=yes + created new head + rebasing 4:c850352cb575 "merge" + 2 new orphan changesets + $ hg log -G -T '{rev} {desc}' + @ 9 merge + |\ + | o 8 modify a + | | + | | * 5 modify b + | | | + +---x 4 merge + | | | + o | | 3 add b + |/ / + | | * 2 change a + | |/ + | x 1 modify a + |/ + o 0 add a + +Target commit has new content + $ hg cat -r 'desc("modify a")' a + a4 +The working copy is clean and there is no unfinished operation + $ hg st -v + + Can abort or continue after conflict while rebasing temporary commit -------------------------------------------------------------------------------- @@ -169,6 +236,13 @@ warning: conflicts while merging a! (edit, then use 'hg resolve --mark') unresolved conflicts (see 'hg resolve', then 'hg amend --continue') [240] + $ hg log -G -T '{rev} {desc}' + % 2 temporary commit for "amend --rev" + | + o 1 modify a + | + @ 0 add a + $ hg st -v M a ? a.orig @@ -216,17 +290,165 @@ $ hg resolve -m (no more unresolved files) continue: hg amend --continue - $ hg continue - 1 new orphan changesets $ hg log -G -T '{rev} {desc}' - o 4 add a + o 2 temporary commit for "amend --rev" + | + o 1 modify a + | + @ 0 add a - @ 1 modify a + $ hg continue + rebasing 1:41c4ea50d4cf "modify a" + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + 1 new orphan changesets + unresolved conflicts (see 'hg resolve', then 'hg amend --continue') + [240] + $ hg log -G -T '{rev} {desc}' + @ 4 add a + + % 1 modify a | x 0 add a + $ echo resolved again > a + $ hg resolve -m + (no more unresolved files) + continue: hg amend --continue + $ hg continue + rebasing 1:41c4ea50d4cf "modify a" + $ hg log -G -T '{rev} {desc}' + @ 5 modify a + | + o 4 add a + Target commit has new content $ hg cat -r 'desc("add a")' a resolved The working copy is clean and there is no unfinished operation $ hg st -v + ? a.orig + + +Can abort or continue after conflict while rebasing descendant commit +-------------------------------------------------------------------------------- + +Common setup for abort and continue + $ cd "$TESTTMP" + $ hg init conflict-rebasing-descendant-commit + $ cd conflict-rebasing-descendant-commit + $ echo a > a + $ hg ci -Aqm 'add a' + $ echo a2 > a + $ hg ci -m 'modify a' + $ echo a > a + $ hg ci -m 'revert a' + $ echo a3 > a + $ hg log -G -T '{rev} {desc}' + @ 2 revert a + | + o 1 modify a + | + o 0 add a + + $ hg amend -r 'desc("add a")' + rebasing 1:41c4ea50d4cf "modify a" + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + 2 new orphan changesets + unresolved conflicts (see 'hg resolve', then 'hg amend --continue') + [240] + $ hg log -G -T '{rev} {desc}' + @ 5 add a + + * 2 revert a + | + % 1 modify a + | + x 0 add a + + $ hg st -v + M a + ? a.orig + # The repository is in an unfinished *amend* state. + + # Unresolved merge conflicts: + # + # a + # + # To mark files as resolved: hg resolve --mark FILE + + # To continue: hg amend --continue + # To abort: hg amend --abort + + +Make a copy of the repo and working copy to test continuing + $ cp -R . ../conflict-rebasing-descendant-commit-continue + +Can abort + $ hg abort + rebase aborted + saved backup bundle to $TESTTMP/conflict-rebasing-descendant-commit/.hg/strip-backup/f242bdc041a8-4658c323-backup.hg +The log output looks like it did before we started + $ hg log -G -T '{rev} {desc}' + @ 2 revert a + | + o 1 modify a + | + o 0 add a + +The working copy has the change it had before we started + $ hg diff + diff -r 43217edd8bde a + --- a/a Thu Jan 01 00:00:00 1970 +0000 + +++ b/a Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -a + +a3 +There is no unfinished operation + $ hg st -v + M a + ? a.orig + +Can continue + $ cd ../conflict-rebasing-descendant-commit-continue + $ echo resolved > a + $ hg resolve -m + (no more unresolved files) + continue: hg amend --continue + $ hg log -G -T '{rev} {desc}' + @ 5 add a + + * 2 revert a + | + * 1 modify a + | + x 0 add a + + $ hg continue + rebasing 1:41c4ea50d4cf "modify a" + rebasing 2:43217edd8bde "revert a" + merging a + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + unresolved conflicts (see 'hg resolve', then 'hg amend --continue') + [240] + $ echo resolved again > a + $ hg resolve -m + (no more unresolved files) + continue: hg amend --continue + $ hg continue + already rebased 1:41c4ea50d4cf "modify a" as 79b758e3d369 + rebasing 2:43217edd8bde "revert a" + $ hg log -G -T '{rev} {desc}' + @ 7 revert a + | + o 6 modify a + | + o 5 add a + +Target commit has new content + $ hg cat -r 'desc("add a")' a + a3 +The working copy is clean and there is no unfinished operation + $ hg st -v + ? a.orig