rebase: add --rev option to rebase
This option allow a strict set of revision to be specified instead of using -s
or -b. Rebase will refuse start if striping rebased changeset will strip non
rebased changeset. Rebase will refuse to work on set with multiple root.
--- a/hgext/rebase.py Sat Oct 15 12:57:47 2011 -0500
+++ b/hgext/rebase.py Sat Oct 15 20:12:32 2011 +0200
@@ -15,7 +15,7 @@
'''
from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
-from mercurial import extensions, patch
+from mercurial import extensions, patch, scmutil
from mercurial.commands import templateopts
from mercurial.node import nullrev
from mercurial.lock import release
@@ -34,6 +34,9 @@
_('rebase from the base of the specified changeset '
'(up to greatest common ancestor of base and dest)'),
_('REV')),
+ ('r', 'rev', [],
+ _('rebase these revisions'),
+ _('REV')),
('d', 'dest', '',
_('rebase onto the specified changeset'), _('REV')),
('', 'collapse', False, _('collapse the rebased changesets')),
@@ -119,6 +122,7 @@
destf = opts.get('dest', None)
srcf = opts.get('source', None)
basef = opts.get('base', None)
+ revf = opts.get('rev', [])
contf = opts.get('continue')
abortf = opts.get('abort')
collapsef = opts.get('collapse', False)
@@ -156,7 +160,13 @@
else:
if srcf and basef:
raise util.Abort(_('cannot specify both a '
+ 'source and a base'))
+ if revf and basef:
+ raise util.Abort(_('cannot specify both a'
'revision and a base'))
+ if revf and srcf:
+ raise util.Abort(_('cannot specify both a'
+ 'revision and a source'))
if detachf:
if not srcf:
raise util.Abort(
@@ -167,24 +177,39 @@
cmdutil.bailifchanged(repo)
if not destf:
- # Destination defaults to the latest revision in the current branch
+ # Destination defaults to the latest revision in the
+ # current branch
branch = repo[None].branch()
dest = repo[branch]
else:
dest = repo[destf]
+ rebaseset = None
if srcf:
revsetargs = ('(%r)::', srcf)
+ elif revf:
+ rebaseset = scmutil.revrange(repo, revf)
+ if not keepf and rebaseset:
+ try:
+ repo.set('children(%ld) - %ld',
+ rebaseset, rebaseset).next()
+ except StopIteration:
+ pass # empty revset is what we look for
+ else:
+ msg = _("can't remove original changesets with"
+ " unrebased descendants")
+ hint = _('use --keep to keep original changesets')
+ raise util.Abort(msg, hint=hint)
else:
base = basef or '.'
revsetargs = ('(children(ancestor(%r, %d)) and ::(%r))::',
base, dest, base)
-
- rebaseset = [c.rev() for c in repo.set(*revsetargs)]
+ if rebaseset is None:
+ rebaseset = [c.rev() for c in repo.set(*revsetargs)]
if rebaseset:
result = buildstate(repo, dest, rebaseset, detachf)
else:
- repo.ui.debug(_('base is ancestor of destination'))
+ repo.ui.debug('base is ancestor of destination')
result = None
if not result:
# Empty state built, nothing to rebase
@@ -545,9 +570,9 @@
detachset = set()
roots = list(repo.set('roots(%ld)', rebaseset))
if not roots:
- raise util.Abort( _('no matching revisions'))
+ raise util.Abort(_('no matching revisions'))
if len(roots) > 1:
- raise util.Abort( _("can't rebase multiple roots"))
+ raise util.Abort(_("can't rebase multiple roots"))
root = roots[0]
commonbase = root.ancestor(dest)
Binary file tests/bundles/rebase-revset.hg has changed
--- a/tests/test-rebase-parameters.t Sat Oct 15 12:57:47 2011 -0500
+++ b/tests/test-rebase-parameters.t Sat Oct 15 20:12:32 2011 +0200
@@ -67,7 +67,7 @@
[255]
$ hg rebase --base 5 --source 4
- abort: cannot specify both a revision and a base
+ abort: cannot specify both a source and a base
[255]
$ hg rebase
--- a/tests/test-rebase-scenario-global.t Sat Oct 15 12:57:47 2011 -0500
+++ b/tests/test-rebase-scenario-global.t Sat Oct 15 20:12:32 2011 +0200
@@ -269,4 +269,240 @@
|/
o 0: 'A'
+ $ cd ..
+Test for revset
+
+We need a bit different graph
+All destination are B
+
+ $ hg init ah
+ $ cd ah
+ $ hg unbundle $TESTDIR/bundles/rebase-revset.hg
+ adding changesets
+ adding manifests
+ adding file changes
+ added 9 changesets with 9 changes to 9 files (+2 heads)
+ (run 'hg heads' to see heads, 'hg merge' to merge)
+ $ hg tglog
+ o 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'
+
+ $ cd ..
+
+
+Simple case with keep:
+
+Source on have two descendant heads but ask for one
+
+ $ hg clone -q -u . ah ah1
+ $ cd ah1
+ $ hg rebase -r '2::8' -d 1
+ abort: can't remove original changesets with unrebased descendants
+ (use --keep to keep original changesets)
+ [255]
+ $ hg rebase -r '2::8' -d 1 --keep
+ $ hg tglog
+ @ 13: 'I'
+ |
+ o 12: 'H'
+ |
+ o 11: 'G'
+ |
+ o 10: 'D'
+ |
+ o 9: 'C'
+ |
+ | o 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'
+
+
+ $ cd ..
+
+Base on have one descendant heads we ask for but common ancestor have two
+
+ $ hg clone -q -u . ah ah2
+ $ cd ah2
+ $ hg rebase -r '3::8' -d 1
+ abort: can't remove original changesets with unrebased descendants
+ (use --keep to keep original changesets)
+ [255]
+ $ hg rebase -r '3::8' -d 1 --keep
+ $ hg tglog
+ @ 12: 'I'
+ |
+ o 11: 'H'
+ |
+ o 10: 'G'
+ |
+ o 9: 'D'
+ |\
+ | | o 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'
+
+
+ $ cd ..
+
+rebase subset
+
+ $ hg clone -q -u . ah ah3
+ $ cd ah3
+ $ hg rebase -r '3::7' -d 1
+ abort: can't remove original changesets with unrebased descendants
+ (use --keep to keep original changesets)
+ [255]
+ $ hg rebase -r '3::7' -d 1 --keep
+ $ hg tglog
+ @ 11: 'H'
+ |
+ o 10: 'G'
+ |
+ o 9: 'D'
+ |\
+ | | o 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'
+
+
+ $ cd ..
+
+rebase subset with multiple head
+
+ $ hg clone -q -u . ah ah4
+ $ cd ah4
+ $ hg rebase -r '3::(7+5)' -d 1
+ abort: can't remove original changesets with unrebased descendants
+ (use --keep to keep original changesets)
+ [255]
+ $ hg rebase -r '3::(7+5)' -d 1 --keep
+ $ hg tglog
+ @ 13: 'H'
+ |
+ o 12: 'G'
+ |
+ | o 11: 'F'
+ | |
+ | o 10: 'E'
+ |/
+ o 9: 'D'
+ |\
+ | | o 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'
+
+
+ $ cd ..
+
+More advanced tests
+
+rebase on ancestor with revset
+
+ $ hg clone -q -u . ah ah5
+ $ cd ah5
+ $ hg rebase -r '6::' -d 2
+ saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg
+ $ hg tglog
+ @ 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'
+
+ $ cd ..
+
+
+rebase with multiple root.
+We rebase E and G on B
+We would expect heads are I, F if it was supported
+
+ $ hg clone -q -u . ah ah6
+ $ cd ah6
+ $ hg rebase -r '(4+6)::' -d 1
+ abort: can't rebase multiple roots
+ [255]
+ $ cd ..