[PATCH evolve-ext] evolve: handle merge commit with single obsolete parent (issue4389)

Andrew Halberstadt halbersa at gmail.com
Fri Dec 18 08:38:57 CST 2015


# HG changeset patch
# User Andrew Halberstadt <ahalberstadt at mozilla.com>
# Date 1448588311 18000
#      Thu Nov 26 20:38:31 2015 -0500
# Node ID 414873a5d2457722b2d473b62136b8f8eb01b0c9
# Parent  52c276d2ddb2f076c84ef8258038ef7b5ee00fe4
evolve: handle merge commit with single obsolete parent (issue4389)

This handles evolving merge commits with a single obsolete parent. Merge
commits with two obsolete parents are still unsupported. Note this depends
on a change to merge.graft in core. Older versions of mercurial will not
have this functionality. Also, test-unstable.t will fail with older
versions.

diff --git a/hgext/evolve.py b/hgext/evolve.py
--- a/hgext/evolve.py
+++ b/hgext/evolve.py
@@ -891,30 +891,32 @@ def rewrite(repo, old, updates, head, ne
         tr.close()
         return newid, created
     finally:
         lockmod.release(tr, lock, wlock)
 
 class MergeFailure(error.Abort):
     pass
 
-def relocate(repo, orig, dest, keepbranch=False):
+def relocate(repo, orig, dest, pctx=None, keepbranch=False):
     """rewrite <rev> on dest"""
     if orig.rev() == dest.rev():
         raise error.Abort(_('tried to relocate a node on top of itself'),
                          hint=_("This shouldn't happen. If you still "
                                 "need to move changesets, please do so "
                                 "manually with nothing to rebase - working "
                                 "directory parent is also destination"))
 
-    if not orig.p2().rev() == node.nullrev:
-        raise error.Abort(
-            'no support for evolving merge changesets yet',
-            hint="Redo the merge and use `hg prune <old> --succ <new>` "
-                 "to obsolete the old one")
+    if pctx is None:
+        if len(orig.parents()) == 2:
+            raise error.Abort(_("tried to relocate a merge commit without "
+                                "specifying which parent should be moved"),
+                              hint=_("Specify the parent by passing in pctx"))
+        pctx = orig.p1()
+
     destbookmarks = repo.nodebookmarks(dest.node())
     nodesrc = orig.node()
     destphase = repo[nodesrc].phase()
     commitmsg = orig.description()
 
     cache = {}
     sha1s = re.findall(sha1re, commitmsg)
     unfi = repo.unfiltered()
@@ -944,17 +946,29 @@ def relocate(repo, orig, dest, keepbranc
         try:
             if repo['.'].rev() != dest.rev():
                 merge.update(repo, dest, False, True, False)
             if bmactive(repo):
                 repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo))
             bmdeactivate(repo)
             if keepbranch:
                 repo.dirstate.setbranch(orig.branch())
-            r = merge.graft(repo, orig, orig.p1(), ['local', 'graft'])
+
+            try:
+                r = merge.graft(repo, orig, pctx, ['local', 'graft'], True)
+            except TypeError:
+                # not using recent enough mercurial
+                if len(orig.parents()) == 2:
+                    raise error.Abort(
+                        _("no support for evolving merge changesets yet"),
+                        hint=_("Redo the merge and use `hg prune <old> --succ "
+                               "<new>` to obsolete the old one"))
+
+                r = merge.graft(repo, orig, pctx, ['local', 'graft'])
+
             if r[-1]:  #some conflict
                 raise error.Abort(
                         'unresolved merge conflicts (see hg help resolve)')
             nodenew = _relocatecommit(repo, orig, commitmsg)
         except error.Abort as exc:
             repo.dirstate.beginparentchange()
             repo.setparents(repo['.'].node(), nullid)
             writedirstate(repo.dirstate, tr)
@@ -1728,23 +1742,30 @@ def _aspiringdescendant(repo, revs):
             if unstable not in result:
                 tovisit.append(unstable)
                 result.add(unstable)
     return sorted(result - target)
 
 def _solveunstable(ui, repo, orig, dryrun=False, confirm=False,
                    progresscb=None):
     """Stabilize a unstable changeset"""
-    obs = orig.parents()[0]
-    if not obs.obsolete() and len(orig.parents()) == 2:
-        obs = orig.parents()[1] # second parent is obsolete ?
-
-    if not obs.obsolete():
+    pctx = orig.p1()
+    if len(orig.parents()) == 2:
+        if not pctx.obsolete():
+            pctx = orig.p2()  # second parent is obsolete ?
+        elif orig.p2().obsolete():
+            raise error.Abort(_("no support for evolving merge changesets "
+                                "with two obsolete parents yet"),
+                              hint=_("Redo the merge and use `hg prune <old> "
+                                   "--succ <new>` to obsolete the old one"))
+
+    if not pctx.obsolete():
         ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
         return False
+    obs = pctx
     newer = obsolete.successorssets(repo, obs.node())
     # search of a parent which is not killed
     while not newer or newer == [()]:
         ui.debug("stabilize target %s is plain dead,"
                  " trying to stabilize on its parent\n" %
                  obs)
         obs = obs.parents()[0]
         newer = obsolete.successorssets(repo, obs.node())
@@ -1780,17 +1801,17 @@ def _solveunstable(ui, repo, orig, dryru
     todo = 'hg rebase -r %s -d %s\n' % (orig, target)
     if dryrun:
         repo.ui.write(todo)
     else:
         repo.ui.note(todo)
         if progresscb: progresscb()
         keepbranch = orig.p1().branch() != orig.branch()
         try:
-            relocate(repo, orig, target, keepbranch)
+            relocate(repo, orig, target, pctx, keepbranch)
         except MergeFailure:
             repo.opener.write('graftstate', orig.hex() + '\n')
             repo.ui.write_err(_('evolve failed!\n'))
             repo.ui.write_err(
                 _('fix conflict and run "hg evolve --continue"'
                   ' or use "hg update -C" to abort\n'))
             raise
 
diff --git a/tests/test-unstable.t b/tests/test-unstable.t
--- a/tests/test-unstable.t
+++ b/tests/test-unstable.t
@@ -98,27 +98,23 @@ Not supported yet
   | x  1:b3264cec9506 at default(draft) add _a
   |/
   o  0:b4952fcf48cf at default(draft) add base
   
 
   $ hg evo --all --any --unstable
   move:[3] merge
   atop:[4] aprime
-  abort: no support for evolving merge changesets yet
-  (Redo the merge and use `hg prune <old> --succ <new>` to obsolete the old one)
-  [255]
+  working directory is now at 0bf3f3a59c8c
   $ hg log -G
-  @  4:47127ea62e5f at default(draft) aprime
-  |
-  | o    3:6b4280e33286 at default(draft) merge
-  | |\
-  +---o  2:474da87dd33b at default(draft) add _c
+  @    5:0bf3f3a59c8c at default(draft) merge
+  |\
+  | o  4:47127ea62e5f at default(draft) aprime
   | |
-  | x  1:b3264cec9506 at default(draft) add _a
+  o |  2:474da87dd33b at default(draft) add _c
   |/
   o  0:b4952fcf48cf at default(draft) add base
   
 
   $ cd ..
 
 ===============================================================================
 Test instability resolution for a merge changeset unstable because both
@@ -153,19 +149,17 @@ Not supported yet
   +---x  2:474da87dd33b at default(draft) add _c
   | |
   | x  1:b3264cec9506 at default(draft) add _a
   |/
   o  0:b4952fcf48cf at default(draft) add base
   
 
   $ hg evo --all --any --unstable
-  move:[3] merge
-  atop:[5] cprime
-  abort: no support for evolving merge changesets yet
+  abort: no support for evolving merge changesets with two obsolete parents yet
   (Redo the merge and use `hg prune <old> --succ <new>` to obsolete the old one)
   [255]
   $ hg log -G
   @  5:2db39fda7e2f at default(draft) cprime
   |
   | o  4:47127ea62e5f at default(draft) aprime
   |/
   | o    3:6b4280e33286 at default(draft) merge


More information about the Mercurial-devel mailing list