tests/test-evolve-orphan-merge.t
author Anton Shestakov <av6@dwimlabs.net>
Tue, 24 Dec 2019 23:31:42 +0700
branchmercurial-4.5
changeset 5037 5ea7c807d094
parent 4676 b6c819facbe8
child 4916 4101fab78314
child 5097 f0c18ad24273
child 5245 207427ed9302
permissions -rw-r--r--
heptapod-ci: skip test-evolve.t in tests-py2-pure job It seems pure mode on Mercurial 4.5 was broken in case of this test. The error was: Traceback (most recent call last): File "/tmp/hgtests.YoK65c/install/bin/hg", line 41, in <module> dispatch.run() File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 88, in run status = (dispatch(req) or 0) & 255 File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 183, in dispatch ret = _runcatch(req) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 324, in _runcatch return _callcatch(ui, _runcatchfunc) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 332, in _callcatch return scmutil.callcatch(ui, func) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/scmutil.py", line 154, in callcatch return func() File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 314, in _runcatchfunc return _dispatch(req) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 918, in _dispatch cmdpats, cmdoptions) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 673, in runcommand ret = _runcommand(ui, options, cmd, d) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 926, in _runcommand return cmdfunc() File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/dispatch.py", line 915, in <lambda> d = lambda: util.checksignature(func)(ui, *args, **strcmdopt) File "/tmp/hgtests.YoK65c/install/lib/python/mercurial/util.py", line 1195, in check return func(*args, **kwargs) File "/builds/mercurial/evolve/hgext3rd/evolve/evolvecmd.py", line 1603, in evolve return _performevolve(ui, repo, **opts) File "/builds/mercurial/evolve/hgext3rd/evolve/evolvecmd.py", line 1726, in _performevolve targetcat, lastsolved) File "/builds/mercurial/evolve/hgext3rd/evolve/evolvecmd.py", line 1752, in _solveonerev lastsolved=lastsolved, stacktmplt=stacktmplt) File "/builds/mercurial/evolve/hgext3rd/evolve/evolvecmd.py", line 80, in _solveone lastsolved=lastsolved) File "/builds/mercurial/evolve/hgext3rd/evolve/evolvecmd.py", line 183, in _solveunstable keepbranch, b'orphan') File "/builds/mercurial/evolve/hgext3rd/evolve/evolvecmd.py", line 952, in relocate fullnode = unfi.changelog.index.partialmatch(sha1) AttributeError: 'InlinedIndexObject' object has no attribute 'partialmatch'

** Testing resolution of orphans by `hg evolve` when merges are involved **

  $ cat >> $HGRCPATH <<EOF
  > [ui]
  > interactive = True
  > [alias]
  > glog = log -GT "{rev}:{node|short} {desc}\n ({bookmarks}) {phase}"
  > [extensions]
  > rebase =
  > EOF
  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext3rd/evolve/" >> $HGRCPATH

Repo Setup

  $ hg init repo
  $ cd repo
  $ echo ".*\.orig" > .hgignore
  $ hg add .hgignore
  $ hg ci -m "added hgignore"

An orphan merge changeset with one of the parent obsoleted
==========================================================

1) When merging both the parents does not result in conflicts
-------------------------------------------------------------

  $ echo foo > a
  $ hg ci -Aqm "added a"
  $ hg up .^
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ echo foo > b
  $ hg ci -Aqm "added b"
  $ hg merge
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  (branch merge, don't forget to commit)
  $ hg ci -m "merging a and b"

  $ hg glog
  @    3:3b2b6f4652ee merging a and b
  |\    () draft
  | o  2:d76850646258 added b
  | |   () draft
  o |  1:c7586e2a9264 added a
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

Testing with obsoleting the second parent

  $ hg up d76850646258
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ echo bar > b
  $ hg amend
  1 new orphan changesets

  $ hg glog
  @  4:64370c9805e7 added b
  |   () draft
  | *    3:3b2b6f4652ee merging a and b
  | |\    () draft
  +---x  2:d76850646258 added b
  | |     () draft
  | o  1:c7586e2a9264 added a
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg evolve --all --update
  move:[3] merging a and b
  atop:[4] added b
  working directory is now at 91fd62122a4b

  $ hg glog
  @    5:91fd62122a4b merging a and b
  |\    () draft
  | o  4:64370c9805e7 added b
  | |   () draft
  o |  1:c7586e2a9264 added a
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg parents
  changeset:   5:91fd62122a4b
  tag:         tip
  parent:      4:64370c9805e7
  parent:      1:c7586e2a9264
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     merging a and b
  

Testing with obsoleting the first parent

  $ hg up c7586e2a9264
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ echo bar > a
  $ hg amend
  1 new orphan changesets

  $ hg glog
  @  6:3d41537b44ca added a
  |   () draft
  | *    5:91fd62122a4b merging a and b
  | |\    () draft
  +---o  4:64370c9805e7 added b
  | |     () draft
  | x  1:c7586e2a9264 added a
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg evolve --all --update
  move:[5] merging a and b
  atop:[6] added a
  working directory is now at 968d205ba4d8

  $ hg glog
  @    7:968d205ba4d8 merging a and b
  |\    () draft
  | o  6:3d41537b44ca added a
  | |   () draft
  o |  4:64370c9805e7 added b
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg parents
  changeset:   7:968d205ba4d8
  tag:         tip
  parent:      6:3d41537b44ca
  parent:      4:64370c9805e7
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     merging a and b
  
2) When merging both the parents resulted in conflicts
------------------------------------------------------

  $ hg up 8fa14d15e168
  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
  $ echo foo > c
  $ hg ci -Aqm "foo to c"
  $ hg prev
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  [0] added hgignore
  $ echo bar > c
  $ hg ci -Aqm "bar to c"

  $ hg glog
  @  9:d0f84b25d4e3 bar to c
  |   () draft
  | o  8:1c165c673853 foo to c
  |/    () draft
  | o    7:968d205ba4d8 merging a and b
  | |\    () draft
  +---o  6:3d41537b44ca added a
  | |     () draft
  | o  4:64370c9805e7 added b
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

Prune old test changesets to have clear graph view
  $ hg prune -r 64370c9805e7 -r 3d41537b44ca -r 968d205ba4d8
  3 changesets pruned

  $ hg glog
  @  9:d0f84b25d4e3 bar to c
  |   () draft
  | o  8:1c165c673853 foo to c
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg merge
  merging c
  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
  use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
  [1]
  $ echo foobar > c
  $ hg resolve -m
  (no more unresolved files)
  $ hg ci -m "foobar to c"

  $ hg glog
  @    10:fd41d25a3e90 foobar to c
  |\    () draft
  | o  9:d0f84b25d4e3 bar to c
  | |   () draft
  o |  8:1c165c673853 foo to c
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

Testing with first parent obsoleted

  $ hg up 1c165c673853
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo FOO > c
  $ hg amend
  1 new orphan changesets

  $ hg glog
  @  11:31c317b7bdb1 foo to c
  |   () draft
  | *    10:fd41d25a3e90 foobar to c
  | |\    () draft
  +---o  9:d0f84b25d4e3 bar to c
  | |     () draft
  | x  8:1c165c673853 foo to c
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg evolve --all --update
  move:[10] foobar to c
  atop:[11] foo to c
  merging c
  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
  unresolved merge conflicts
  (see 'hg help evolve.interrupted')
  [1]

  $ echo FOObar > c
  $ hg resolve -m
  (no more unresolved files)
  continue: hg evolve --continue
  $ hg evolve --continue
  evolving 10:fd41d25a3e90 "foobar to c"
  working directory is now at c5405d2da7a1

  $ hg glog
  @    12:c5405d2da7a1 foobar to c
  |\    () draft
  | o  11:31c317b7bdb1 foo to c
  | |   () draft
  o |  9:d0f84b25d4e3 bar to c
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg parents
  changeset:   12:c5405d2da7a1
  tag:         tip
  parent:      9:d0f84b25d4e3
  parent:      11:31c317b7bdb1
  user:        test
  date:        Thu Jan 01 00:00:00 1970 +0000
  summary:     foobar to c
  
Testing a conlficting merge with second parent obsoleted

  $ hg up 31c317b7bdb1
  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
  $ echo foo > c
  $ hg amend
  1 new orphan changesets

  $ hg glog
  @  13:928097d0b5b5 foo to c
  |   () draft
  | *    12:c5405d2da7a1 foobar to c
  | |\    () draft
  +---x  11:31c317b7bdb1 foo to c
  | |     () draft
  | o  9:d0f84b25d4e3 bar to c
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg evolve --all --update
  move:[12] foobar to c
  atop:[13] foo to c
  merging c
  warning: conflicts while merging c! (edit, then use 'hg resolve --mark')
  unresolved merge conflicts
  (see 'hg help evolve.interrupted')
  [1]

  $ echo foobar > c
  $ hg resolve -m
  (no more unresolved files)
  continue: hg evolve --continue

  $ hg evolve --continue
  evolving 12:c5405d2da7a1 "foobar to c"
  working directory is now at dc1948a6eeab

  $ hg glog
  @    14:dc1948a6eeab foobar to c
  |\    () draft
  | o  13:928097d0b5b5 foo to c
  | |   () draft
  o |  9:d0f84b25d4e3 bar to c
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

3) When stabilizing other changesets resulted in orphan merge changeset
-----------------------------------------------------------------------

  $ hg prune -r d0f84b25d4e3 -r 928097d0b5b5 -r dc1948a6eeab
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  working directory is now at 8fa14d15e168
  3 changesets pruned

  $ for ch in l m; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
  $ hg up 8fa14d15e168
  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
  $ for ch in x y; do echo foo > $ch; hg ci -Aqm "added "$ch; done;
  $ hg glog
  @  18:863d11043c67 added y
  |   () draft
  o  17:3f2247835c1d added x
  |   () draft
  | o  16:e44dc179e7f5 added m
  | |   () draft
  | o  15:8634bee7bf1e added l
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg merge
  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
  (branch merge, don't forget to commit)
  $ hg ci -m "merge commit"

  $ hg up 8634bee7bf1e
  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
  $ echo bar > l
  $ hg amend
  2 new orphan changesets

  $ hg glog
  @  20:fccc9de66799 added l
  |   () draft
  | *    19:190763373d8b merge commit
  | |\    () draft
  | | o  18:863d11043c67 added y
  | | |   () draft
  +---o  17:3f2247835c1d added x
  | |     () draft
  | *  16:e44dc179e7f5 added m
  | |   () draft
  | x  15:8634bee7bf1e added l
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft
  $ hg evolve --all --update
  move:[16] added m
  atop:[20] added l
  move:[19] merge commit
  working directory is now at a446ad3e6700

  $ hg glog
  @    22:a446ad3e6700 merge commit
  |\    () draft
  | o  21:495d2039f8f1 added m
  | |   () draft
  | o  20:fccc9de66799 added l
  | |   () draft
  o |  18:863d11043c67 added y
  | |   () draft
  o |  17:3f2247835c1d added x
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

4) When both the parents of the merge changeset are obsolete with a succ
------------------------------------------------------------------------

  $ hg prune -r a446ad3e6700 -r 495d2039f8f1 -r 863d11043c67
  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
  working directory is now at fccc9de66799
  3 changesets pruned

  $ hg glog
  @  20:fccc9de66799 added l
  |   () draft
  | o  17:3f2247835c1d added x
  |/    () 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 "merged l and x"

  $ hg up fccc9de66799
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ echo foobar > l
  $ hg amend
  1 new orphan changesets
  $ hg up 3f2247835c1d
  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
  $ echo bar > x
  $ hg amend
  $ hg glog
  @  25:cdf6547da25f added x
  |   () draft
  | o  24:3f371171d767 added l
  |/    () draft
  | *    23:7b78a9784f3e merged l and x
  | |\    () draft
  +---x  20:fccc9de66799 added l
  | |     () draft
  | x  17:3f2247835c1d added x
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

XXX: We should handle this case too
  $ hg evolve --all --update
  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 glog
  @    27:adb665a78e08 merged l and x
  |\    () draft
  | o  25:cdf6547da25f added x
  | |   () draft
  o |  24:3f371171d767 added l
  |/    () draft
  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
  @    27:adb665a78e08 merged l and x
  |\    () draft
  | x  25:cdf6547da25f added x
  | |   () draft
  o |  24:3f371171d767 added l
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg evolve --rev .
  move:[27] merged l and x
  atop:[0] added hgignore
  working directory is now at fb8fe870ae7d

  $ hg glog
  @    28:fb8fe870ae7d merged l and x
  |\    () draft
  | o  24:3f371171d767 added l
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

6) When one parent is pruned without successor and the other parent of merge is
the parent of the pruned commit
--------------------------------------------------------------------------------

  $ hg glog
  @    28:fb8fe870ae7d merged l and x
  |\    () draft
  | o  24:3f371171d767 added l
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

  $ hg prune -r 3f371171d767
  1 changesets pruned
  1 new orphan changesets

  $ hg glog
  @    28:fb8fe870ae7d merged l and x
  |\    () draft
  | x  24:3f371171d767 added l
  |/    () draft
  o  0:8fa14d15e168 added hgignore
      () draft

This is the right thing to do here. When you have a merge changeset, and one
parent is pruned and parent of that pruned parent is same as another parent of
the merge changeset, that should lead to merge changeset being a non-merge
changeset and non-pruned changeset as its only parent

If you look at the above graph, the side part:

\
 x
/

is redundant now as the changeset is pruned and we should remove this chain
while evolving.

This case can occur a lot of times in workflows where people make branches and
merge them again. After getting their work done, they may want to get rid of
that branch and they prune all their changeset, which will result in this
case where merge commit becomes orphan with its ancestors pruned up until a
point where the other parent of merge is the first non-pruned ancestor.

  $ hg evolve -r .
  move:[28] merged l and x
  atop:[0] added hgignore
  working directory is now at b61ba77b924a

  $ hg glog
  @  29:b61ba77b924a merged l and x
  |   () draft
  o  0:8fa14d15e168 added hgignore
      () draft

7) When one parent is pruned without successor and has no parent
----------------------------------------------------------------

  $ hg prune -r .
  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
  working directory is now at 8fa14d15e168
  1 changesets pruned
  $ hg up null
  0 files updated, 0 files merged, 1 files removed, 0 files unresolved

  $ echo foo > foo
  $ hg add foo
  $ hg ci -m "added foo"
  created new head

  $ 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
  @    31:32beb84b9dbc merge commit
  |\    () draft
  | x  30:f3ba8b99bb6f added foo
  |     () draft
  o  0:8fa14d15e168 added hgignore
      () draft

The current behavior seems to be the correct behavior in the above case. This is
also another case which can arise flow merge workflow where people start a
branch from null changeset and merge it and then prune it or get rid of it.

Also if you look at the above graph, the side part:

\
 x

becomes redundant as the changeset is pruned without successor and we should
just remove that chain.

  $ hg evolve -r .
  move:[31] merge commit
  atop:[-1] 
  working directory is now at d2a03dd8c951

  $ hg glog
  @  32:d2a03dd8c951 merge commit
  |   () draft
  o  0:8fa14d15e168 added hgignore
      () draft