Patchwork D11039: amend: make `hg amend -r` rebase descendants of target onto amended target

login
register
mail settings
Submitter phabricator
Date July 9, 2021, 8:28 p.m.
Message ID <differential-rev-PHID-DREV-sk6522nsxprllxjwha2n-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/49353/
State New
Headers show

Comments

phabricator - July 9, 2021, 8:28 p.m.
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/D11039

AFFECTED FILES
  hgext/amend.py
  tests/test-amend-rev.t

CHANGE DETAILS




To: martinvonz, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

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
@@ -48,14 +48,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}'
+  o  6 add b (known-bad-output !)
+  |
   @  5 modify a (known-bad-output !)
-  | (known-bad-output !)
-  | *  2 add b (known-bad-output !)
-  | | (known-bad-output !)
-  | x  1 modify a (known-bad-output !)
-  |/ (known-bad-output !)
+  |
   o  0 add a
   
 Target commit has new content
@@ -86,6 +84,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
@@ -133,17 +138,165 @@ 
   $ hg resolve -m
   (no more unresolved files)
   continue: hg amend --continue
+  $ hg log -G -T '{rev} {desc}'
+  o  2 temporary commit for "amend --rev"
+  |
+  o  1 modify a
+  |
+  @  0 add 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 (known-bad-output !)
-  | (known-bad-output !)
+  %  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}'
+  o  5 modify a (known-bad-output !)
+  |
+  @  4 add a (known-bad-output !)
+  
 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}'
+  o  7 revert a (known-bad-output !)
+  |
+  o  6 modify a
+  |
+  @  5 add a (known-bad-output !)
+  
+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
diff --git a/hgext/amend.py b/hgext/amend.py
--- a/hgext/amend.py
+++ b/hgext/amend.py
@@ -208,11 +208,17 @@ 
     unfi = repo.unfiltered()
     target_ctx = unfi[state[b'target_node']]
     temp_ctx = unfi[state[b'temp_node']]
+    wc_pctx = temp_ctx.p1()
 
     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, target_ctx, wc_pctx
+    )
 
 
 def _rebase_temp_node(ui, repo, state, rebase, temp_ctx, target_ctx):
@@ -275,6 +281,36 @@ 
     return amended_ctx
 
 
+def _rebase_descendants(
+    ui, repo, state, rebase, amended_ctx, target_ctx, wc_pctx
+):
+    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:
+        with statemod.delegating(repo, b'amend', b'rebase'):
+            ret = rebase.rebase(
+                ui,
+                repo,
+                rev=[
+                    revsetlang.formatspec(
+                        b'only(%d, %d)', wc_pctx.rev(), target_ctx.rev()
+                    )
+                ],
+                dest=revsetlang.formatspec(b'%d', amended_ctx.rev()),
+            )
+
+    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):
     with repo.wlock(), repo.lock(), repo.transaction(b'amend'):
         state_store = statemod.cmdstate(repo, b'amend-state')
@@ -299,6 +335,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)