[PATCH 3 of 3 two-rebase-fixes] rebase: properly handle unrebased revision between rebased one
Pierre-Yves David
pierre-yves.david at ens-lyon.org
Fri Jan 18 22:50:59 UTC 2013
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1358548908 -3600
# Node ID 6e0c775e67c12b22a58dfc771e4e00784eaf1523
# Parent 124ad8551b284a700309b2c4cbcffb6b36385605
rebase: properly handle unrebased revision between rebased one
With rebase taking multiple roots it is possible to have revision in the "rebase
domain" not rebased themself. We do not want rebased revision above them to be
detached. We want such revision to be rebased on the nearest rebased ancestors.
This allows to preserve the topology of the rebase set as much a possible
To achieve this we introduce a new state `revignored` which informs
`defineparents` of the situation.
The test in `test-rebase-obsolete.t` was actually wrote and his now fixed.
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -21,10 +21,11 @@ from mercurial.node import nullrev
from mercurial.lock import release
from mercurial.i18n import _
import os, errno
nullmerge = -2
+revignored = -3
cmdtable = {}
command = cmdutil.command(cmdtable)
testedwith = 'internal'
@@ -390,10 +391,19 @@ def rebasenode(repo, rev, p1, state, col
base = repo[rev].p1().node()
# When collapsing in-place, the parent is the common ancestor, we
# have to allow merging with it.
return merge.update(repo, rev, True, True, False, base, collapse)
+def nearestrebased(repo, rev, state):
+ """return the nearest ancestors of rev in the rebase result"""
+ rebased = [r for r in state if state[r] > nullmerge]
+ candidates = repo.revs('max(%ld and (::%d))', rebased, rev)
+ if candidates:
+ return state[candidates[0]]
+ else:
+ return None
+
def defineparents(repo, rev, target, state, targetancestors):
'Return the new parent relationship of the revision that will be rebased'
parents = repo[rev].parents()
p1 = p2 = nullrev
@@ -401,10 +411,14 @@ def defineparents(repo, rev, target, sta
if P1n in targetancestors:
p1 = target
elif P1n in state:
if state[P1n] == nullmerge:
p1 = target
+ elif state[P1n] == revignored:
+ p1 = nearestrebased(repo, P1n, state)
+ if p1 is None:
+ p1 = target
else:
p1 = state[P1n]
else: # P1n external
p1 = target
p2 = P1n
@@ -413,10 +427,15 @@ def defineparents(repo, rev, target, sta
P2n = parents[1].rev()
# interesting second parent
if P2n in state:
if p1 == target: # P1n in targetancestors or external
p1 = state[P2n]
+ elif state[P2n] == revignored:
+ p2 = nearestrebased(repo, P2n, state)
+ if p2 is None:
+ # no ancestors rebased yet, detach
+ p2 = target
else:
p2 = state[P2n]
else: # P2n external
if p2 != nullrev: # P1n external too => rev is a merged revision
raise util.Abort(_('cannot use revision %d as base, result '
@@ -530,14 +549,14 @@ def restorestatus(repo):
keep = bool(int(l))
elif i == 5:
keepbranches = bool(int(l))
else:
oldrev, newrev = l.split(':')
- if newrev != str(nullmerge):
+ if newrev in (str(nullmerge), str(revignored)):
+ state[repo[oldrev].rev()] = int(newrev)
+ else:
state[repo[oldrev].rev()] = repo[newrev].rev()
- else:
- state[repo[oldrev].rev()] = int(newrev)
skipped = set()
# recompute the set of skipped revs
if not collapse:
seen = set([target])
for old, new in sorted(state.items()):
@@ -656,10 +675,19 @@ def buildstate(repo, dest, rebaseset, co
detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
[root.rev()]))
for r in detachset:
if r not in state:
state[r] = nullmerge
+ if len(roots) > 1:
+ # If we have multiple roots, we may have "hole" in the rebase set.
+ # Rebase roots that descend from those "hole" should not be detached as
+ # other root are. We use the special `revignored` to inform rebase that
+ # the revision should be ignored but that `defineparent` should search
+ # a rebase destination that make sense regarding rebaset topology.
+ rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
+ for ignored in set(rebasedomain) - set(rebaseset):
+ state[ignored] = revignored
return repo['.'].rev(), dest.rev(), state
def clearrebased(ui, repo, state, skipped, collapsedas=None):
"""dispose of rebased revision at the end of the rebase
diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t
--- a/tests/test-rebase-obsolete.t
+++ b/tests/test-rebase-obsolete.t
@@ -364,15 +364,15 @@ Test that rewriting leaving instability
Test multiple root handling
------------------------------------
$ hg rebase --dest 4 --rev '7+11+9'
$ hg log -G
- @ 14:00891d85fcfc C
+ @ 14:1e8370e38cca C
|
| o 13:102b4c1d889b D
- |/
- | o 12:bfe264faf697 H
+ | |
+ o | 12:bfe264faf697 H
|/
| o 10:7c6027df6a99 B
| |
| x 7:02de42196ebe H
| |
More information about the Mercurial-devel
mailing list