D11037: amend: make `hg amend -r` rebase temporary commit onto target commit
martinvonz (Martin von Zweigbergk)
phabricator at mercurial-scm.org
Fri Jul 9 20:28:46 UTC 2021
martinvonz created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.
REPOSITORY
rHG Mercurial
BRANCH
default
REVISION DETAIL
https://phab.mercurial-scm.org/D11037
AFFECTED FILES
hgext/amend.py
mercurial/state.py
tests/test-amend-rev.t
CHANGE DETAILS
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
@@ -2,6 +2,7 @@
$ cat << EOF >> $HGRCPATH
> [extensions]
> amend=
+ > rebase=
> debugdrawdag=$TESTDIR/drawdag.py
> [experimental]
> evolution.createmarkers=True
@@ -31,9 +32,15 @@
nothing changed
[1]
+Fails if rebase is not enabled
+
+ $ echo a3 > a
+ $ hg amend -r '.^' --config extensions.rebase=!
+ config error: amend --rev requires the rebase extension to be enabled
+ [30]
+
Fails if evolution is not enabled
- $ echo a3 > a
$ hg amend -r 'desc("modify a")' --config experimental.evolution.createmarkers=False
abort: --rev requires evolution.createmarkers to be enabled
[20]
@@ -42,10 +49,10 @@
$ hg amend -r 'desc("modify a")'
$ hg log -G -T '{rev} {desc}'
- @ 3 temporary commit for "amend --rev" (known-bad-output !)
+ @ 4 temporary commit for "amend --rev" (known-bad-output !)
| (known-bad-output !)
- o 2 add b
- |
+ | o 2 add b (known-bad-output !)
+ |/ (known-bad-output !)
o 1 modify a
|
o 0 add a
@@ -56,3 +63,88 @@
a3 (missing-correct-output !)
The working copy is clean and there is no unfinished operation
$ hg st -v
+
+
+Can abort or continue after conflict while rebasing temporary commit
+--------------------------------------------------------------------------------
+
+Common setup for abort and continue
+ $ cd "$TESTTMP"
+ $ hg init conflict-rebasing-temp-commit
+ $ cd conflict-rebasing-temp-commit
+ $ echo a > a
+ $ hg ci -Aqm 'add a'
+ $ echo a2 > a
+ $ hg ci -m 'modify a'
+ $ echo a3 > a
+ $ hg log -G -T '{rev} {desc}'
+ @ 1 modify a
+ |
+ o 0 add a
+
+ $ hg amend -r 'desc("add a")'
+ warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+ unresolved conflicts (see 'hg resolve', then 'hg amend --continue')
+ [240]
+ $ 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-temp-commit-continue
+
+Can abort
+ $ hg abort
+ rebase aborted
+ saved backup bundle to $TESTTMP/conflict-rebasing-temp-commit/.hg/strip-backup/5d5724f8921e-27ec44bb-backup.hg
+The log output looks like it did before we started
+ $ hg log -G -T '{rev} {desc}'
+ @ 1 modify a
+ |
+ o 0 add a
+
+The working copy has the change it had before we started
+ $ hg diff
+ diff -r 41c4ea50d4cf 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 @@
+ -a2
+ +a3
+There is no unfinished operation
+ $ hg st -v
+ M a
+ ? a.orig
+
+Can continue
+ $ cd ../conflict-rebasing-temp-commit-continue
+ $ echo resolved > a
+ $ hg resolve -m
+ (no more unresolved files)
+ continue: hg amend --continue
+ $ hg continue
+ $ hg log -G -T '{rev} {desc}'
+ @ 3 temporary commit for "amend --rev" (known-bad-output !)
+ | (known-bad-output !)
+ | o 1 modify a (known-bad-output !)
+ |/ (known-bad-output !)
+ o 0 add a
+
+Target commit has new content
+ $ hg cat -r 'desc("add a")' a
+ a (known-bad-output !)
+ resolved (missing-correct-output !)
+The working copy is clean and there is no unfinished operation
+ $ hg st -v
+ ? a.orig
diff --git a/mercurial/state.py b/mercurial/state.py
--- a/mercurial/state.py
+++ b/mercurial/state.py
@@ -373,6 +373,22 @@
return c.continuefunc(ui, repo)
+def abortchild(ui, repo, opname, childopname):
+ """Checks that childopname is in progress, and aborts it."""
+
+ p, c = _getparentandchild(opname, childopname)
+ if not ischildunfinished(repo, opname, childopname):
+ raise error.ProgrammingError(
+ _(b'child op %s of parent %s is not unfinished')
+ % (childopname, opname)
+ )
+ if not c.abortfunc:
+ raise error.ProgrammingError(
+ _(b'op %s has no abort function') % childopname
+ )
+ return c.abortfunc(ui, repo)
+
+
addunfinished(
b'update',
fname=b'updatestate',
diff --git a/hgext/amend.py b/hgext/amend.py
--- a/hgext/amend.py
+++ b/hgext/amend.py
@@ -17,10 +17,12 @@
cmdutil,
commands,
error,
+ extensions,
merge,
obsolete,
registrar,
repair,
+ revsetlang,
rewriteutil,
scmutil,
state as statemod,
@@ -151,6 +153,7 @@
_(b'--rev requires evolution.createmarkers to be enabled')
)
+ rebase = _find_rebase()
state = {}
state_store = statemod.cmdstate(repo, b'amend-state')
@@ -172,6 +175,7 @@
temp_ctx = repo[b'tip']
state[b'target_node'] = target_ctx.node()
state[b'temp_node'] = temp_ctx.node()
+ _do_continue_amend_rev(ui, repo, state, rebase)
except error.InterventionRequired:
raise
except Exception:
@@ -179,8 +183,56 @@
raise
+def _find_rebase():
+ try:
+ return extensions.find(b'rebase')
+ except KeyError:
+ raise error.ConfigError(
+ b'amend --rev requires the rebase extension to be enabled'
+ )
+
+
def _continue_amend_rev(ui, repo):
- raise error.Abort(_(b'--continue is not yet implemented'))
+ rebase = _find_rebase()
+
+ state_store = statemod.cmdstate(repo, b'amend-state')
+ state = state_store.read()
+
+ with repo.wlock(), repo.lock(), util.acceptintervention(
+ repo.transaction(b'amend')
+ ), state_store.save_on_conflicts(1, state):
+ _do_continue_amend_rev(ui, repo, state, rebase)
+
+
+def _do_continue_amend_rev(ui, repo, state, rebase):
+ unfi = repo.unfiltered()
+ target_ctx = unfi[state[b'target_node']]
+ temp_ctx = unfi[state[b'temp_node']]
+
+ _rebase_temp_node(ui, repo, state, rebase, temp_ctx, target_ctx)
+
+
+def _rebase_temp_node(ui, repo, state, rebase, temp_ctx, target_ctx):
+ if b'rebased_temp_node' in state:
+ return repo.unfiltered()[state[b'rebased_temp_node']]
+ elif statemod.ischildunfinished(repo, b'amend', b'rebase'):
+ with ui.silent(), statemod.delegating(repo, b'amend', b'rebase'):
+ ret = statemod.continuechild(ui, repo, b'amend', b'rebase')
+ elif temp_ctx.p1() != target_ctx:
+ with ui.silent(), statemod.delegating(repo, b'amend', b'rebase'):
+ ret = rebase.rebase(
+ ui,
+ repo,
+ rev=[revsetlang.formatspec(b'%d', temp_ctx.rev())],
+ dest=revsetlang.formatspec(b'%d', target_ctx.rev()),
+ )
+
+ if ret:
+ raise error.Abort(_(b'failed to rebase temporary commit'))
+
+ rebased_temp_ctx = repo[b'tip']
+ state[b'rebased_temp_node'] = rebased_temp_ctx.node()
+ return rebased_temp_ctx
def _abort_amend_rev(ui, repo):
@@ -193,20 +245,35 @@
def _do_abort_amend_rev(ui, repo, state):
unfi = repo.unfiltered()
+ if statemod.ischildunfinished(repo, b'amend', b'rebase'):
+ with ui.silent():
+ statemod.abortchild(ui, repo, b'amend', b'rebase')
+ to_strip = []
temp_node = state.get(b'temp_node')
if temp_node and temp_node in unfi:
+ to_strip.append(temp_node)
temp_ctx = unfi[temp_node]
merge.clean_update(temp_ctx)
with repo.dirstate.parentchange():
- scmutil.movedirstate(repo, temp_ctx.p1())
- repair.delayedstrip(ui, repo, [temp_node])
+ scmutil.movedirstate(unfi, temp_ctx.p1())
+ rebased_temp_node = state.get(b'rebased_temp_node')
+ to_strip.append(rebased_temp_node)
+ to_strip = [node for node in to_strip if node and node in unfi]
+ if to_strip:
+ repair.delayedstrip(ui, unfi, to_strip)
def extsetup(ui):
- statemod.addunfinished(
- b'amend',
- fname=b'amend-state',
- allowcommit=False,
- abortfunc=_abort_amend_rev,
- continuefunc=_continue_amend_rev,
- )
+ def _after_rebase_loaded(loaded):
+ if loaded:
+ statemod.addunfinished(
+ b'amend',
+ fname=b'amend-state',
+ allowcommit=False,
+ childopnames=[b'rebase'],
+ abortfunc=_abort_amend_rev,
+ continueflag=True,
+ continuefunc=_continue_amend_rev,
+ )
+
+ extensions.afterloaded(b'rebase', _after_rebase_loaded)
To: martinvonz, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
More information about the Mercurial-devel
mailing list