rebase: avoid redundant merges (
issue1301)
--- a/hgext/rebase.py Mon Oct 06 14:41:22 2008 +0200
+++ b/hgext/rebase.py Tue Sep 16 17:51:14 2008 +0200
@@ -13,12 +13,31 @@
http://www.selenic.com/mercurial/wiki/index.cgi/RebaseProject
'''
-from mercurial import util, repair, merge, cmdutil, dispatch, commands, extensions
+from mercurial import util, repair, merge, cmdutil, dispatch, commands
+from mercurial import extensions, ancestor
from mercurial.commands import templateopts
from mercurial.node import nullrev
from mercurial.i18n import _
import os, errno
+def rebasemerge(repo, rev, first=False):
+ 'return the correct ancestor'
+ oldancestor = ancestor.ancestor
+
+ def newancestor(a, b, pfunc):
+ ancestor.ancestor = oldancestor
+ anc = ancestor.ancestor(a, b, pfunc)
+ if b == rev:
+ return repo[rev].parents()[0].rev()
+ return ancestor.ancestor(a, b, pfunc)
+
+ if not first:
+ ancestor.ancestor = newancestor
+ else:
+ repo.ui.debug(_("First revision, do not change ancestor\n"))
+ stats = merge.update(repo, rev, True, True, False)
+ return stats
+
def rebase(ui, repo, **opts):
"""move changeset (and descendants) to a different branch
@@ -116,10 +135,12 @@
"""Skip commit if collapsing has been required and rev is not the last
revision, commit otherwise
"""
- repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
+ repo.ui.debug(_(" set parents\n"))
+ if collapse and not last:
+ repo.dirstate.setparents(repo[p1].node())
+ return None
- if collapse and not last:
- return None
+ repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
# Commit, record the old nodeid
m, a, r = repo.status()[:3]
@@ -147,16 +168,25 @@
def rebasenode(repo, rev, target, state, skipped, targetancestors, collapse):
'Rebase a single revision'
- repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev].node()))
+ repo.ui.debug(_("rebasing %d:%s\n") % (rev, repo[rev]))
p1, p2 = defineparents(repo, rev, target, state, targetancestors)
+ repo.ui.debug(_(" future parents are %d and %d\n") % (repo[p1].rev(),
+ repo[p2].rev()))
+
# Merge phase
if len(repo.parents()) != 2:
# Update to target and merge it with local
- merge.update(repo, p1, False, True, False)
+ if repo['.'].rev() != repo[p1].rev():
+ repo.ui.debug(_(" update to %d:%s\n") % (repo[p1].rev(), repo[p1]))
+ merge.update(repo, p1, False, True, False)
+ else:
+ repo.ui.debug(_(" already in target\n"))
repo.dirstate.write()
- stats = merge.update(repo, rev, True, False, False)
+ repo.ui.debug(_(" merge against %d:%s\n") % (repo[rev].rev(), repo[rev]))
+ first = repo[rev].rev() == repo[min(state)].rev()
+ stats = rebasemerge(repo, rev, first)
if stats[3] > 0:
raise util.Abort(_('fix unresolved conflicts with hg resolve then '
--- a/tests/test-rebase-collapse Mon Oct 06 14:41:22 2008 +0200
+++ b/tests/test-rebase-collapse Tue Sep 16 17:51:14 2008 +0200
@@ -41,15 +41,20 @@
createrepo > /dev/null 2>&1
hg glog --template '{rev}: {desc}\n'
-echo '% Rebasing'
+echo '% Rebasing B onto H'
hg up -C 3
hg rebase --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/'
hg glog --template '{rev}: {desc}\n'
+echo "Expected A, B, C, D, F, H"
+hg manifest
createrepo > /dev/null 2>&1
-echo '% Rebasing'
+echo
+echo '% Rebasing G onto H'
hg rebase --base 6 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/'
hg glog --template '{rev}: {desc}\n'
+echo "Expected A, E, F, H"
+hg manifest
createrepocomplex () {
cd $BASE
@@ -76,6 +81,7 @@
addcommit "H" 7
}
+echo
createrepocomplex > /dev/null 2>&1
hg glog --template '{rev}: {desc}\n'
@@ -84,8 +90,91 @@
hg rebase -s 2 --collapse
echo
-echo '% Rebase and collapse'
+echo '% Rebase and collapse - E onto H'
hg rebase -s 4 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/'
hg glog --template '{rev}: {desc}\n'
+echo "Expected A, B, C, E, F, H"
+hg manifest
+createrepocomplex () {
+ cd $BASE
+ rm -rf a
+ hg init a
+ cd a
+ addcommit "A" 0
+ addcommit "B" 1
+
+ hg up 0
+ addcommit "C" 2
+ hg merge
+ commit "D" 3
+
+ hg up 1
+ addcommit "E" 4
+
+ echo "F" > E
+ commit "F" 5
+
+ addcommit "G" 6
+
+ hg merge
+ commit "H" 7
+
+ hg up 0
+ addcommit "I" 8
+}
+
+echo
+createrepocomplex > /dev/null 2>&1
+hg glog --template '{rev}: {desc}\n'
+
+echo
+echo '% Rebase and collapse - E onto I'
+hg rebase -s 4 --collapse
+
+echo '% Fix conflict and continue'
+echo 'Resolved merge' > E
+hg resolve -m E
+hg rebase -c 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+
+hg glog --template '{rev}: {desc}\n'
+
+echo "Expected A, B, C, E, G, I"
+hg manifest
+
+echo 'Cat E:'
+cat E
+
+createrepocomplex () {
+ cd $BASE
+ rm -rf a
+ hg init a
+ cd a
+ addcommit "A" 0
+ addcommit "B" 1
+
+ addcommit "C" 2
+ hg up 1
+
+ addcommit "D" 3
+
+ hg merge
+ commit "E" 4
+
+ hg up 0
+ addcommit "F" 5
+}
+
+echo
+createrepocomplex > /dev/null 2>&1
+hg glog --template '{rev}: {desc}\n'
+
+echo
+echo '% Rebase and collapse - B onto F'
+hg rebase -s 1 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+
+hg glog --template '{rev}: {desc}\n'
+
+echo "Expected A, B, C, D, F"
+hg manifest
exit 0
--- a/tests/test-rebase-collapse.out Mon Oct 06 14:41:22 2008 +0200
+++ b/tests/test-rebase-collapse.out Tue Sep 16 17:51:14 2008 +0200
@@ -14,7 +14,7 @@
|/
o 0: A
-% Rebasing
+% Rebasing B onto H
3 files updated, 0 files merged, 2 files removed, 0 files unresolved
saving bundle to
adding branch
@@ -37,7 +37,15 @@
|/
o 0: A
-% Rebasing
+Expected A, B, C, D, F, H
+A
+B
+C
+D
+F
+H
+
+% Rebasing G onto H
saving bundle to
adding branch
adding changesets
@@ -60,6 +68,12 @@
|/
o 0: A
+Expected A, E, F, H
+A
+E
+F
+H
+
@ 7: H
|
| o 6: G
@@ -80,7 +94,7 @@
% Rebase and collapse - more than one external (fail)
abort: unable to collapse, there is more than one external parent
-% Rebase and collapse
+% Rebase and collapse - E onto H
saving bundle to
adding branch
adding changesets
@@ -102,3 +116,104 @@
|/
o 0: A
+Expected A, B, C, E, F, H
+A
+B
+C
+E
+F
+H
+
+@ 8: I
+|
+| o 7: H
+| |\
+| | o 6: G
+| | |
+| | o 5: F
+| | |
+| | o 4: E
+| | |
+| o | 3: D
+| |\|
+| o | 2: C
+|/ /
+| o 1: B
+|/
+o 0: A
+
+
+% Rebase and collapse - E onto I
+merging E
+warning: conflicts during merge.
+merging E failed!
+abort: fix unresolved conflicts with hg resolve then run hg rebase --continue
+% Fix conflict and continue
+saving bundle to
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 3 changes to 3 files
+rebase completed
+@ 5: Collapsed revision
+|\ * E
+| | * F
+| | * G
+| | * H
+| o 4: I
+| |
+o | 3: D
+|\ \
+| o | 2: C
+| |/
+o / 1: B
+|/
+o 0: A
+
+Expected A, B, C, E, G, I
+A
+B
+C
+E
+G
+I
+Cat E:
+Resolved merge
+
+@ 5: F
+|
+| o 4: E
+| |\
+| | o 3: D
+| | |
+| o | 2: C
+| |/
+| o 1: B
+|/
+o 0: A
+
+
+% Rebase and collapse - B onto F
+saving bundle to
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 2 changesets with 4 changes to 4 files
+rebase completed
+@ 2: Collapsed revision
+| * B
+| * C
+| * D
+| * E
+o 1: F
+|
+o 0: A
+
+Expected A, B, C, D, F
+A
+B
+C
+D
+F
--- a/tests/test-rebase-conflicts Mon Oct 06 14:41:22 2008 +0200
+++ b/tests/test-rebase-conflicts Tue Sep 16 17:51:14 2008 +0200
@@ -31,6 +31,10 @@
mv common.new common
hg commit -d '4 0' -u test -m "L2"
+echo 'l3' >> extra2
+hg add extra2
+hg commit -d '5 0' -u test -m "L3"
+
hg glog --template '{rev}: {desc}\n'
echo
@@ -47,9 +51,28 @@
echo
echo '% Conclude rebase'
-echo 'solved merge' >common
+echo 'resolved merge' >common
hg resolve -m common
hg rebase --continue 2>&1 | cleanoutput
+hg glog --template '{rev}: {desc}\n'
-hg glog --template '{rev}: {desc}\n'
+echo
+echo '% Check correctness'
+echo ' - Rev. 0'
+hg cat -r 0 common
+
+echo ' - Rev. 1'
+hg cat -r 1 common
+
+echo ' - Rev. 2'
+hg cat -r 2 common
+
+echo ' - Rev. 3'
+hg cat -r 3 common
+
+echo ' - Rev. 4'
+hg cat -r 4 common
+
+echo ' - Rev. 5'
+hg cat -r 5 common
--- a/tests/test-rebase-conflicts.out Mon Oct 06 14:41:22 2008 +0200
+++ b/tests/test-rebase-conflicts.out Tue Sep 16 17:51:14 2008 +0200
@@ -1,6 +1,8 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
created new head
-@ 4: L2
+@ 5: L3
+|
+o 4: L2
|
o 3: L1
|
@@ -29,9 +31,11 @@
adding changesets
adding manifests
adding file changes
-added 2 changesets with 2 changes to 2 files
+added 3 changesets with 3 changes to 3 files
rebase completed
-@ 4: L2
+@ 5: L3
+|
+o 4: L2
|
o 3: L1
|
@@ -41,3 +45,22 @@
|
o 0: C1
+
+% Check correctness
+ - Rev. 0
+c1
+ - Rev. 1
+c1
+c2
+ - Rev. 2
+c1
+c2
+c3
+ - Rev. 3
+c1
+c2
+c3
+ - Rev. 4
+resolved merge
+ - Rev. 5
+resolved merge