[PATCH 2 of 2] rebase: add --cherrypick to cherrypick single revisions
Stefano Tortarolo
stefano.tortarolo at gmail.com
Thu Dec 31 01:50:17 UTC 2009
# HG changeset patch
# User Stefano Tortarolo <stefano.tortarolo at gmail.com>
# Date 1262222630 -3600
# Node ID 4643de06fb4d42ea22055d42ce9ff46762f30555
# Parent fcedd4d4282f33be5f86cd3a311bf937fe07528e
rebase: add --cherrypick to cherrypick single revisions
Using the idea behind --detach, rebase can now 'copy' single revisions from
a branch to another one.
Basically, it ignores every changeset up to the requested one, by means of null
merges, and then rebases just the specified changeset.
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -76,6 +76,7 @@
keepf = opts.get('keep', False)
keepbranchesf = opts.get('keepbranches', False)
detachf = opts.get('detach', False)
+ cherryf = opts.get('cherrypick', False)
if contf or abortf:
if contf and abortf:
@@ -89,12 +90,16 @@
raise error.ParseError(
'rebase', _('cannot use detach with continue or abort'))
+ if cherryf:
+ raise error.ParseError(
+ 'rebase', _('cannot use cherrypick with continue or abort'))
+
if srcf or basef or destf:
raise error.ParseError('rebase',
_('abort and continue do not allow specifying revisions'))
- (originalwd, target, state, collapsef, keepf,
- keepbranchesf, detach, external) = restorestatus(repo)
+ (originalwd, target, state, collapsef, keepf, keepbranchesf,
+ detach, cherryf, external) = restorestatus(repo)
if abortf:
abort(repo, originalwd, target, state)
return
@@ -113,8 +118,22 @@
raise error.ParseError(
'rebase', _('cannot specify a base with detach'))
+ if cherryf:
+ if not srcf:
+ raise error.ParseError(
+ 'rebase', _('cherrypick requires a revision to be '
+ 'specified'))
+ if collapsef:
+ raise error.ParseError(
+ 'rebase', _('cannot use collapse with cherrypick'))
+ if basef:
+ raise error.ParseError(
+ 'rebase', _('cannot specify a base with cherrypick'))
+ detachf = True
+
cmdutil.bail_if_changed(repo)
- result = buildstate(repo, destf, srcf, basef, collapsef, detachf)
+ result = buildstate(repo, destf, srcf, basef, collapsef, detachf,
+ cherryf)
if result:
originalwd, target, state, detach, external = result
else: # Empty state built, nothing to rebase
@@ -135,7 +154,7 @@
for rev in sorted(state):
if state[rev] == -1:
storestatus(repo, originalwd, target, state, collapsef, keepf,
- keepbranchesf, detach, external)
+ keepbranchesf, detach, cherryf, external)
rebasenode(repo, rev, target, state, skipped, targetancestors,
collapsef, detach, extrafn)
ui.note(_('rebase merging completed\n'))
@@ -149,7 +168,7 @@
if 'qtip' in repo.tags():
updatemq(repo, state, skipped, **opts)
- if not keepf:
+ if not keepf and not cherryf:
# Remove no more useful revisions
if set(repo.changelog.descendants(min(state))) - set(state):
ui.warn(_("warning: new changesets detected on source branch, "
@@ -326,7 +345,7 @@
repo.mq.save_dirty()
def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
- detach, external):
+ detach, cherry, external):
'Store the current status to allow recovery'
f = repo.opener("rebasestate", "w")
f.write(repo[originalwd].hex() + '\n')
@@ -336,6 +355,7 @@
f.write('%d\n' % int(collapse))
f.write('%d\n' % int(keep))
f.write('%d\n' % int(keepbranches))
+ f.write('%d\n' % int(cherry))
for d, v in state.iteritems():
oldrev = repo[d].hex()
newrev = repo[v].hex()
@@ -371,12 +391,14 @@
keep = bool(int(l))
elif i == 6:
keepbranches = bool(int(l))
+ elif i == 7:
+ cherry = bool(int(l))
else:
oldrev, newrev = l.split(':')
state[repo[oldrev].rev()] = repo[newrev].rev()
repo.ui.debug('rebase status resumed\n')
return (originalwd, target, state, collapse, keep, keepbranches,
- detach, external)
+ detach, cherry, external)
except IOError, err:
if err.errno != errno.ENOENT:
raise
@@ -397,7 +419,7 @@
clearstatus(repo)
repo.ui.status(_('rebase aborted\n'))
-def buildstate(repo, dest, src, base, collapse, detach):
+def buildstate(repo, dest, src, base, collapse, detach, cherry):
'Define which revisions are going to be rebased and where'
targetancestors = set()
detachrev = nullrev
@@ -454,11 +476,20 @@
source = min(rebasingbranch)
if detachrev:
- repo.ui.debug('rebase onto %d starting from %d\n' % (dest, detachrev))
+ if cherry:
+ repo.ui.debug('rebase %d onto %d\n' % (detachrev, dest))
+ else:
+ repo.ui.debug('rebase onto %d starting from %d\n' % (dest, detachrev))
else:
repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
- state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
external = nullrev
+ if cherry:
+ srcdescendants = set(repo.changelog.descendants(source))
+ cherrydescendants = set(repo.changelog.descendants(detachrev))
+ staterevs = srcdescendants - cherrydescendants
+ state = dict.fromkeys(staterevs, nullrev)
+ else:
+ state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
if collapse:
if not targetancestors:
targetancestors = set(repo.changelog.ancestors(dest))
@@ -515,9 +546,10 @@
('', 'keep', False, _('keep original changesets')),
('', 'keepbranches', False, _('keep original branch names')),
('', 'detach', False, _('force detaching of source from its original branch')),
+ ('', 'cherrypick', False, _('cherrypick the revision specified')),
('c', 'continue', False, _('continue an interrupted rebase')),
('a', 'abort', False, _('abort an interrupted rebase')),] +
templateopts,
- _('hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] '
+ _('hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] '
'[--keepbranches] | [-c] | [-a]')),
}
diff --git a/tests/test-rebase-detach b/tests/test-rebase-detach
--- a/tests/test-rebase-detach
+++ b/tests/test-rebase-detach
@@ -56,4 +56,31 @@
echo "Expected A, B, C, D, E"
hg manifest
+echo
+createrepo > /dev/null 2>&1
+hg glog --template '{rev}: {desc}\n'
+echo '% Cherry picking C onto E'
+hg rebase --cherrypick -s 2 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+hg glog --template '{rev}: {desc}\n'
+echo "Expected A, C, E"
+hg manifest
+
+echo
+createrepo > /dev/null 2>&1
+hg glog --template '{rev}: {desc}\n'
+echo '% Cherry picking B onto E'
+hg rebase --cherrypick -s 1 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+hg glog --template '{rev}: {desc}\n'
+echo "Expected A, B, E"
+hg manifest
+
+echo
+createrepo > /dev/null 2>&1
+hg glog --template '{rev}: {desc}\n'
+echo '% Cherry picking D onto E'
+hg rebase --cherrypick -s 3 -d 4 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+hg glog --template '{rev}: {desc}\n'
+echo "Expected A, D, E"
+hg manifest
+
exit 0
diff --git a/tests/test-rebase-detach.out b/tests/test-rebase-detach.out
--- a/tests/test-rebase-detach.out
+++ b/tests/test-rebase-detach.out
@@ -100,3 +100,91 @@
C
D
E
+
+@ 4: E
+|
+| o 3: D
+| |
+| o 2: C
+| |
+| o 1: B
+|/
+o 0: A
+
+% Cherry picking C onto E
+rebase completed
+@ 5: C
+|
+o 4: E
+|
+| o 3: D
+| |
+| o 2: C
+| |
+| o 1: B
+|/
+o 0: A
+
+Expected A, C, E
+A
+C
+E
+
+@ 4: E
+|
+| o 3: D
+| |
+| o 2: C
+| |
+| o 1: B
+|/
+o 0: A
+
+% Cherry picking B onto E
+rebase completed
+@ 5: B
+|
+o 4: E
+|
+| o 3: D
+| |
+| o 2: C
+| |
+| o 1: B
+|/
+o 0: A
+
+Expected A, B, E
+A
+B
+E
+
+@ 4: E
+|
+| o 3: D
+| |
+| o 2: C
+| |
+| o 1: B
+|/
+o 0: A
+
+% Cherry picking D onto E
+not in dirstate: E
+rebase completed
+@ 5: D
+|
+o 4: E
+|
+| o 3: D
+| |
+| o 2: C
+| |
+| o 1: B
+|/
+o 0: A
+
+Expected A, D, E
+A
+D
+E
diff --git a/tests/test-rebase-parameters b/tests/test-rebase-parameters
--- a/tests/test-rebase-parameters
+++ b/tests/test-rebase-parameters
@@ -65,6 +65,14 @@
echo "% Specify detach and collapse"
hg rebase --detach --source 2 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+echo
+echo "% Specify cherrypick and not source"
+hg rebase --cherrypick 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+
+echo
+echo "% Specify cherrypick and collapse"
+hg rebase --cherrypick --source 2 --collapse 2>&1 | sed 's/\(saving bundle to \).*/\1/'
+
echo "% ----------"
echo "% These work"
echo
diff --git a/tests/test-rebase-parameters.out b/tests/test-rebase-parameters.out
--- a/tests/test-rebase-parameters.out
+++ b/tests/test-rebase-parameters.out
@@ -2,7 +2,7 @@
% Use continue and abort
hg rebase: cannot use both abort and continue
-hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] [--keepbranches] | [-c] | [-a]
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
move changeset (and descendants) to a different branch
@@ -22,6 +22,7 @@
--keep keep original changesets
--keepbranches keep original branch names
--detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
-c --continue continue an interrupted rebase
-a --abort abort an interrupted rebase
--style display using template map file
@@ -31,7 +32,7 @@
% Use continue and collapse
hg rebase: cannot use collapse with continue or abort
-hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] [--keepbranches] | [-c] | [-a]
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
move changeset (and descendants) to a different branch
@@ -51,6 +52,7 @@
--keep keep original changesets
--keepbranches keep original branch names
--detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
-c --continue continue an interrupted rebase
-a --abort abort an interrupted rebase
--style display using template map file
@@ -60,7 +62,7 @@
% Use continue/abort and dest/source
hg rebase: abort and continue do not allow specifying revisions
-hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] [--keepbranches] | [-c] | [-a]
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
move changeset (and descendants) to a different branch
@@ -80,6 +82,7 @@
--keep keep original changesets
--keepbranches keep original branch names
--detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
-c --continue continue an interrupted rebase
-a --abort abort an interrupted rebase
--style display using template map file
@@ -89,7 +92,7 @@
% Use source and base
hg rebase: cannot specify both a revision and a base
-hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] [--keepbranches] | [-c] | [-a]
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
move changeset (and descendants) to a different branch
@@ -109,6 +112,7 @@
--keep keep original changesets
--keepbranches keep original branch names
--detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
-c --continue continue an interrupted rebase
-a --abort abort an interrupted rebase
--style display using template map file
@@ -125,7 +129,7 @@
% Specify detach and not source
hg rebase: detach requires a revision to be specified
-hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] [--keepbranches] | [-c] | [-a]
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
move changeset (and descendants) to a different branch
@@ -145,6 +149,7 @@
--keep keep original changesets
--keepbranches keep original branch names
--detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
-c --continue continue an interrupted rebase
-a --abort abort an interrupted rebase
--style display using template map file
@@ -154,7 +159,7 @@
% Specify detach and collapse
hg rebase: cannot use collapse with detach
-hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach] [--keep] [--keepbranches] | [-c] | [-a]
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
move changeset (and descendants) to a different branch
@@ -174,6 +179,67 @@
--keep keep original changesets
--keepbranches keep original branch names
--detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
+ -c --continue continue an interrupted rebase
+ -a --abort abort an interrupted rebase
+ --style display using template map file
+ --template display with template
+
+use "hg -v help rebase" to show global options
+
+% Specify cherrypick and not source
+hg rebase: cherrypick requires a revision to be specified
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
+
+move changeset (and descendants) to a different branch
+
+ Rebase uses repeated merging to graft changesets from one part of history
+ onto another. This can be useful for linearizing local changes relative to
+ a master development tree.
+
+ If a rebase is interrupted to manually resolve a merge, it can be
+ continued with --continue/-c or aborted with --abort/-a.
+
+options:
+
+ -s --source rebase from a given revision
+ -b --base rebase from the base of a given revision
+ -d --dest rebase onto a given revision
+ --collapse collapse the rebased changesets
+ --keep keep original changesets
+ --keepbranches keep original branch names
+ --detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
+ -c --continue continue an interrupted rebase
+ -a --abort abort an interrupted rebase
+ --style display using template map file
+ --template display with template
+
+use "hg -v help rebase" to show global options
+
+% Specify cherrypick and collapse
+hg rebase: cannot use collapse with cherrypick
+hg rebase [-s REV | -b REV] [-d REV] [--collapse | --detach | --cherrypick] [--keep] [--keepbranches] | [-c] | [-a]
+
+move changeset (and descendants) to a different branch
+
+ Rebase uses repeated merging to graft changesets from one part of history
+ onto another. This can be useful for linearizing local changes relative to
+ a master development tree.
+
+ If a rebase is interrupted to manually resolve a merge, it can be
+ continued with --continue/-c or aborted with --abort/-a.
+
+options:
+
+ -s --source rebase from a given revision
+ -b --base rebase from the base of a given revision
+ -d --dest rebase onto a given revision
+ --collapse collapse the rebased changesets
+ --keep keep original changesets
+ --keepbranches keep original branch names
+ --detach force detaching of source from its original branch
+ --cherrypick cherrypick the revision specified
-c --continue continue an interrupted rebase
-a --abort abort an interrupted rebase
--style display using template map file
More information about the Mercurial-devel
mailing list