evolve: handle stabilization of merge changeset with both parents obsoleted
authorPulkit Goyal <7895pulkit@gmail.com>
Sun, 18 Mar 2018 17:29:32 +0530
changeset 3573 d53277cdc1e7
parent 3572 7934e9751d69
child 3574 8aba29d8b133
evolve: handle stabilization of merge changeset with both parents obsoleted This patch adds logic to stabilize an orphan merge changeset having both parents obsoleted. The logic tries to first stabilize the merge changeset on successor of second parent and then stabilize the new changeset formed on the successor of first parent. We are stabilizing on second parent first and then to second parent to preserve the first parent's successor as first parent of the merge changeset. Conflicts can occcur and we can loose processing information, therefore we store a variable in statefile `orphanmerge` which represents whether we are processing a merge changeset with both parents obsoleted. Thanks to Pierre-Yves David for suggesting this way and helping to understand the correctness of this. More rigourous test cases for this will be added in next patch.
hgext3rd/evolve/evolvecmd.py
tests/test-evolve-orphan-merge.t
--- a/hgext3rd/evolve/evolvecmd.py	Mon Mar 19 13:11:55 2018 +0530
+++ b/hgext3rd/evolve/evolvecmd.py	Sun Mar 18 17:29:32 2018 +0530
@@ -99,12 +99,14 @@
         elif not p2obs and p1obs:
             pass
         else:
-            hint = _("Redo the merge (%s) and use `hg prune <old> "
-                     "--succ <new>` to obsolete the old one") % orig.hex()[:12]
-            ui.warn(_("warning: no support for evolving merge changesets "
-                      "with two obsolete parents yet\n") +
-                    _("(%s)\n") % hint)
-            return (False, '')
+            # store that we are resolving an orphan merge with both parents
+            # obsolete and proceed with first parent
+            evolvestate['orphanmerge'] = True
+            # we should process the second parent first, so that in case of
+            # no-conflicts the first parent is processed later and preserved as
+            # first parent
+            pctx = orig.p2()
+            keepbranch = orig.p2().branch() != orig.branch()
 
     if not pctx.obsolete():
         ui.warn(_("cannot solve instability of %s, skipping\n") % orig)
@@ -1154,7 +1156,7 @@
         # cbor does not know how to serialize sets, using list for skippedrevs
         stateopts = {'category': targetcat, 'replacements': {}, 'revs': revs,
                      'confirm': confirmopt, 'startnode': startnode.node(),
-                     'skippedrevs': [], 'command': 'evolve'}
+                     'skippedrevs': [], 'command': 'evolve', 'orphanmerge': False}
         evolvestate.addopts(stateopts)
         for rev in revs:
             curctx = repo[rev]
@@ -1166,6 +1168,19 @@
                 evolvestate['replacements'][curctx.node()] = [ret[1]]
             else:
                 evolvestate['skippedrevs'].append(curctx.node())
+
+            if evolvestate['orphanmerge']:
+                # we were processing an orphan merge with both parents obsolete,
+                # stabilized for second parent, re-stabilize for the first parent
+                ret = _solveone(ui, repo, repo[ret[1]], evolvestate, dryrunopt,
+                                confirmopt, progresscb, targetcat)
+                if ret[0]:
+                    evolvestate['replacements'][curctx.node()] = [ret[1]]
+                else:
+                    evolvestate['skippedrevs'].append(curctx.node())
+
+                evolvestate['orphanmerge'] = False
+
     progresscb()
     _cleanup(ui, repo, startnode, showprogress)
 
@@ -1212,9 +1227,12 @@
                                              currentp1)
 
             else:
-                # both the parents were obsolete, not sure what logic will
-                # required here
-                # XXX: add tests for this and see what is required here
+                # both the parents were obsoleted, if orphanmerge is set, we
+                # are processing the second parent first (to keep parent order)
+                if evolvestate.get('orphanmerge'):
+                    with repo.dirstate.parentchange():
+                        repo.dirstate.setparents(ctxparents[0].node(),
+                                                 currentp1)
                 pass
 
         with repo.ui.configoverride(overrides, 'evolve-continue'):
@@ -1232,6 +1250,12 @@
             category = evolvestate['category']
             confirm = evolvestate['confirm']
             unfi = repo.unfiltered()
+            if evolvestate['orphanmerge']:
+                # processing a merge changeset with both parents obsoleted,
+                # stabilized on second parent, insert in front of list to
+                # re-process to stabilize on first parent
+                evolvestate['revs'].insert(0, repo[node].rev())
+                evolvestate['orphanmerge'] = False
             for rev in evolvestate['revs']:
                 # XXX: prevent this lookup by storing nodes instead of revnums
                 curctx = unfi[rev]
--- a/tests/test-evolve-orphan-merge.t	Mon Mar 19 13:11:55 2018 +0530
+++ b/tests/test-evolve-orphan-merge.t	Sun Mar 18 17:29:32 2018 +0530
@@ -417,21 +417,14 @@
 
 XXX: We should handle this case too
   $ hg evolve --all
-  warning: no support for evolving merge changesets with two obsolete parents yet
-  (Redo the merge (7b78a9784f3e) and use `hg prune <old> --succ <new>` to obsolete the old one)
-
-5) When one of the merge parent is pruned without a successor
--------------------------------------------------------------
+  move:[23] merged l and x
+  atop:[25] added x
+  move:[26] merged l and x
+  atop:[24] added l
+  working directory is now at adb665a78e08
 
-  $ hg prune -r 7b78a9784
-  1 changesets pruned
-
-  $ hg merge
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
-  $ hg ci -m "merged l and x"
   $ hg glog
-  @    26:47e57ebc80aa merged l and x
+  @    27:adb665a78e08 merged l and x
   |\    () draft
   | o  25:cdf6547da25f added x
   | |   () draft
@@ -440,11 +433,40 @@
   o  0:8fa14d15e168 added hgignore
       () draft
 
+  $ hg exp
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID adb665a78e08b962cff415301058d782086c0f33
+  # Parent  3f371171d767ef79cf85d156cf46d4035960fcf0
+  # Parent  cdf6547da25f1ca5d01102302ad713f444547b48
+  merged l and x
+  
+  diff -r 3f371171d767 -r adb665a78e08 x
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/x	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +bar
+
+  $ hg parents
+  changeset:   27:adb665a78e08
+  tag:         tip
+  parent:      24:3f371171d767
+  parent:      25:cdf6547da25f
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     merged l and x
+  
+
+5) When one of the merge parent is pruned without a successor
+-------------------------------------------------------------
+
   $ hg prune -r cdf6547da25f
   1 changesets pruned
   1 new orphan changesets
   $ hg glog
-  @    26:47e57ebc80aa merged l and x
+  @    27:adb665a78e08 merged l and x
   |\    () draft
   | x  25:cdf6547da25f added x
   | |   () draft
@@ -454,12 +476,12 @@
       () draft
 
   $ hg evolve --rev .
-  move:[26] merged l and x
+  move:[27] merged l and x
   atop:[0] added hgignore
-  working directory is now at c117a030135c
+  working directory is now at fb8fe870ae7d
 
   $ hg glog
-  @    27:c117a030135c merged l and x
+  @    28:fb8fe870ae7d merged l and x
   |\    () draft
   | o  24:3f371171d767 added l
   |/    () draft
@@ -471,7 +493,7 @@
 --------------------------------------------------------------------------------
 
   $ hg glog
-  @    27:c117a030135c merged l and x
+  @    28:fb8fe870ae7d merged l and x
   |\    () draft
   | o  24:3f371171d767 added l
   |/    () draft
@@ -483,7 +505,7 @@
   1 new orphan changesets
 
   $ hg glog
-  @    27:c117a030135c merged l and x
+  @    28:fb8fe870ae7d merged l and x
   |\    () draft
   | x  24:3f371171d767 added l
   |/    () draft
@@ -511,12 +533,12 @@
 point where the other parent of merge is the first non-pruned ancestor.
 
   $ hg evolve -r .
-  move:[27] merged l and x
+  move:[28] merged l and x
   atop:[0] added hgignore
-  working directory is now at 57b29ecd607c
+  working directory is now at b61ba77b924a
 
   $ hg glog
-  @  28:57b29ecd607c merged l and x
+  @  29:b61ba77b924a merged l and x
   |   () draft
   o  0:8fa14d15e168 added hgignore
       () draft
@@ -530,33 +552,32 @@
   1 changesets pruned
   $ hg up null
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ hg glog
-  o  0:8fa14d15e168 added hgignore
-      () draft
 
   $ echo foo > foo
   $ hg add foo
   $ hg ci -m "added foo"
   created new head
-  $ hg glog
-  @  29:f3ba8b99bb6f added foo
-      () draft
-  o  0:8fa14d15e168 added hgignore
-      () draft
 
   $ hg merge
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg ci -m "merge commit"
+  $ hg glog
+  @    31:32beb84b9dbc merge commit
+  |\    () draft
+  | o  30:f3ba8b99bb6f added foo
+  |     () draft
+  o  0:8fa14d15e168 added hgignore
+      () draft
 
   $ hg prune -r f3ba8b99bb6f
   1 changesets pruned
   1 new orphan changesets
 
   $ hg glog
-  @    30:32beb84b9dbc merge commit
+  @    31:32beb84b9dbc merge commit
   |\    () draft
-  | x  29:f3ba8b99bb6f added foo
+  | x  30:f3ba8b99bb6f added foo
   |     () draft
   o  0:8fa14d15e168 added hgignore
       () draft
@@ -574,12 +595,12 @@
 just remove that chain.
 
   $ hg evolve -r .
-  move:[30] merge commit
+  move:[31] merge commit
   atop:[-1] 
   working directory is now at d2a03dd8c951
 
   $ hg glog
-  @  31:d2a03dd8c951 merge commit
+  @  32:d2a03dd8c951 merge commit
   |   () draft
   o  0:8fa14d15e168 added hgignore
       () draft