##// END OF EJS Templates
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
rebase: do not add second parent to rebased changeset (drop detach option) (BC) Rebase now behaves as if --detach was always passed. Non-merges are rebased as non-merges, regardless of their parent being an ancestor of the destination. Merges will usually be rebased as merges unless both of their parents are ancestors of the destination, or one of their parents is pruned when rebased. This only alters the behavior of rebase when using the --source/--rev options. --detach option is deprecated. All test changes were carefully validated.

File last commit:

r17005:50f43451 default
r17005:50f43451 default
Show More
rebase.py
702 lines | 27.8 KiB | text/x-python | PythonLexer
Stefano Tortarolo
Add rebase extension
r6906 # rebase.py - rebasing feature for mercurial
#
# Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Stefano Tortarolo
Add rebase extension
r6906
Dirkjan Ochtman
extensions: change descriptions for extensions providing a few commands
r8934 '''command to move sets of revisions to a different ancestor
Stefano Tortarolo
Add rebase extension
r6906
Martin Geisler
rebase: word-wrap help texts at 70 characters
r7999 This extension lets you rebase changesets in an existing Mercurial
repository.
Stefano Tortarolo
Add rebase extension
r6906
For more information:
Martin Geisler
rebase: link to RebaseExtension...
r9301 http://mercurial.selenic.com/wiki/RebaseExtension
Stefano Tortarolo
Add rebase extension
r6906 '''
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884 from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks
Alain Leufroy <alain.leufroyATgmailMYDOTcom>
rebase: fix phases movement...
r15917 from mercurial import extensions, patch, scmutil, phases
Stefano Tortarolo
Add rebase extension
r6906 from mercurial.commands import templateopts
from mercurial.node import nullrev
Ronny Pfannschmidt
switch lock releasing in the extensions from gc to explicit
r8112 from mercurial.lock import release
Stefano Tortarolo
Add rebase extension
r6906 from mercurial.i18n import _
import os, errno
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 nullmerge = -2
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 cmdtable = {}
command = cmdutil.command(cmdtable)
Augie Fackler
hgext: mark all first-party extensions as such
r16743 testedwith = 'internal'
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306
@command('rebase',
[('s', 'source', '',
_('rebase from the specified changeset'), _('REV')),
('b', 'base', '',
_('rebase from the base of the specified changeset '
'(up to greatest common ancestor of base and dest)'),
_('REV')),
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 ('r', 'rev', [],
_('rebase these revisions'),
_('REV')),
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 ('d', 'dest', '',
_('rebase onto the specified changeset'), _('REV')),
('', 'collapse', False, _('collapse the rebased changesets')),
('m', 'message', '',
_('use text as collapse commit message'), _('TEXT')),
Matt Mackall
rebase: add --edit switch
r15219 ('e', 'edit', False, _('invoke editor on commit messages')),
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 ('l', 'logfile', '',
_('read collapse commit message from file'), _('FILE')),
('', 'keep', False, _('keep original changesets')),
('', 'keepbranches', False, _('keep original branch names')),
Pierre-Yves David
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
r17005 ('D', 'detach', False, _('(DEPRECATED)')),
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 ('t', 'tool', '', _('specify merge tool')),
('c', 'continue', False, _('continue an interrupted rebase')),
('a', 'abort', False, _('abort an interrupted rebase'))] +
templateopts,
_('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
'hg rebase {-a|-c}'))
Stefano Tortarolo
Add rebase extension
r6906 def rebase(ui, repo, **opts):
"""move changeset (and descendants) to a different branch
Martin Geisler
rebase: word-wrap help texts at 70 characters
r7999 Rebase uses repeated merging to graft changesets from one part of
Greg Ward
rebase: improve help text...
r10646 history (the source) onto another (the destination). This can be
Martin Geisler
rebase: stress that only local changesets should be rebased
r11188 useful for linearizing *local* changes relative to a master
Greg Ward
rebase: improve help text...
r10646 development tree.
Martin Geisler
rebase: stress that only local changesets should be rebased
r11188 You should not rebase changesets that have already been shared
with others. Doing so will force everybody else to perform the
same rebase or they will end up with duplicated changesets after
pulling in your rebased changesets.
Greg Ward
rebase: improve help text...
r10646 If you don't specify a destination changeset (``-d/--dest``),
rebase uses the tipmost head of the current named branch as the
destination. (The destination changeset is not modified by
rebasing, but new changesets are added as its descendants.)
You can specify which changesets to rebase in two ways: as a
Martin Geisler
rebase: remove unnecessary \" from help string...
r10659 "source" changeset or as a "base" changeset. Both are shorthand
for a topologically related set of changesets (the "source
branch"). If you specify source (``-s/--source``), rebase will
rebase that changeset and all of its descendants onto dest. If you
specify base (``-b/--base``), rebase will select ancestors of base
back to but not including the common ancestor with dest. Thus,
``-b`` is less precise but more convenient than ``-s``: you can
specify any changeset in the source branch, and rebase will select
the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
uses the parent of the working directory as the base.
Greg Ward
rebase: improve help text...
r10646
By default, rebase recreates the changesets in the source branch
as descendants of dest and then destroys the originals. Use
``--keep`` to preserve the original source changesets. Some
changesets in the source branch (e.g. merges from the destination
branch) may be dropped if they no longer contribute any change.
One result of the rules for selecting the destination changeset
and source branch is that, unlike ``merge``, rebase will do
nothing if you are at the latest (tipmost) head of a named branch
with two heads. You need to explicitly specify source and/or
destination (or ``update`` to the other head, if it's the head of
the intended source branch).
Stefano Tortarolo
Add rebase extension
r6906
Martin Geisler
rebase: word-wrap help texts at 70 characters
r7999 If a rebase is interrupted to manually resolve a merge, it can be
Martin Geisler
help texts: write command line switches as -a/--abc
r8076 continued with --continue/-c or aborted with --abort/-a.
Matt Mackall
rebase: add error codes...
r11205
Returns 0 on success, 1 if nothing to rebase.
Stefano Tortarolo
Add rebase extension
r6906 """
Benoit Boissinot
remove unused variables
r7280 originalwd = target = None
Stefano Tortarolo
Add rebase extension
r6906 external = nullrev
Benoit Boissinot
rebase: use set instead of dict
r8454 state = {}
skipped = set()
Stefano Tortarolo
rebase: refactoring...
r10351 targetancestors = set()
Stefano Tortarolo
Add rebase extension
r6906
Matt Mackall
rebase: add --edit switch
r15219 editor = None
if opts.get('edit'):
editor = cmdutil.commitforceeditor
Stefano Tortarolo
Add rebase extension
r6906 lock = wlock = None
try:
Mads Kiilerich
rebase: take locks in the right order...
r15874 wlock = repo.wlock()
Stefano Tortarolo
Add rebase extension
r6906 lock = repo.lock()
# Validate input and define rebasing points
destf = opts.get('dest', None)
srcf = opts.get('source', None)
basef = opts.get('base', None)
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 revf = opts.get('rev', [])
Stefano Tortarolo
Add rebase extension
r6906 contf = opts.get('continue')
abortf = opts.get('abort')
collapsef = opts.get('collapse', False)
Idan Kamara
cmdutil, logmessage: use ui.fin when reading from '-'
r14635 collapsemsg = cmdutil.logmessage(ui, opts)
Martin Geisler
rebase: don't use util.Abort for an internal error
r13609 extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 keepf = opts.get('keep', False)
keepbranchesf = opts.get('keepbranches', False)
Stefano Tortarolo
rebase: add option to not commit after a collapsing...
r10677 # keepopen is not meant for use on the command line, but by
# other extensions
keepopen = opts.get('keepopen', False)
Augie Fackler
rebase: add support to keep branch names...
r7468
Radomir Dopieralski
rebase: add -m/--message to rebase --collapse (issue2389)...
r13661 if collapsemsg and not collapsef:
raise util.Abort(
_('message can only be specified with collapse'))
Stefano Tortarolo
Add rebase extension
r6906 if contf or abortf:
if contf and abortf:
Matt Mackall
rebase: use usual util.abort rather than error.ParseError
r11285 raise util.Abort(_('cannot use both abort and continue'))
Stefano Tortarolo
Add rebase extension
r6906 if collapsef:
Matt Mackall
rebase: use usual util.abort rather than error.ParseError
r11285 raise util.Abort(
_('cannot use collapse with continue or abort'))
Martin Geisler
remove unnecessary outer parenthesis in if-statements
r8117 if srcf or basef or destf:
Matt Mackall
rebase: use usual util.abort rather than error.ParseError
r11285 raise util.Abort(
Stefano Tortarolo
Add rebase extension
r6906 _('abort and continue do not allow specifying revisions'))
Stefano Tortarolo
rebase: add --tool argument for specifying merge tool
r13856 if opts.get('tool', False):
ui.warn(_('tool option will be ignored\n'))
Stefano Tortarolo
Add rebase extension
r6906
Benoit Boissinot
rebase: recompute the set of skipped rev when using --continue (issue2330)
r11843 (originalwd, target, state, skipped, collapsef, keepf,
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 keepbranchesf, external) = restorestatus(repo)
Stefano Tortarolo
Add rebase extension
r6906 if abortf:
Matt Mackall
rebase: add error codes...
r11205 return abort(repo, originalwd, target, state)
Stefano Tortarolo
Add rebase extension
r6906 else:
if srcf and basef:
Matt Mackall
rebase: use usual util.abort rather than error.ParseError
r11285 raise util.Abort(_('cannot specify both a '
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 'source and a base'))
if revf and basef:
Wagner Bruna
rebase: fix typos
r15289 raise util.Abort(_('cannot specify both a '
Matt Mackall
rebase: use usual util.abort rather than error.ParseError
r11285 'revision and a base'))
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 if revf and srcf:
Wagner Bruna
rebase: fix typos
r15289 raise util.Abort(_('cannot specify both a '
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 'revision and a source'))
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352
Matt Mackall
cmdutil: bail_if_changed to bailifchanged
r14289 cmdutil.bailifchanged(repo)
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267
if not destf:
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 # Destination defaults to the latest revision in the
# current branch
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 branch = repo[None].branch()
dest = repo[branch]
else:
Patrick Mezard
rebase: make --dest understand revsets
r16566 dest = scmutil.revsingle(repo, destf)
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267
Matt Mackall
rebase: simplify set generation
r15271 if revf:
Matt Mackall
localrepo: convert various repo.set() users to repo.revs()
r15404 rebaseset = repo.revs('%lr', revf)
Matt Mackall
rebase: simplify set generation
r15271 elif srcf:
Steven Brown
rebase: reinstate old-style rev spec support for the source and base (issue3181)...
r15800 src = scmutil.revrange(repo, [srcf])
Matt Mackall
merge with stable
r15801 rebaseset = repo.revs('(%ld)::', src)
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 else:
Steven Brown
rebase: reinstate old-style rev spec support for the source and base (issue3181)...
r15800 base = scmutil.revrange(repo, [basef or '.'])
Matt Mackall
merge with stable
r15801 rebaseset = repo.revs(
'(children(ancestor(%ld, %d)) and ::(%ld))::',
Matt Mackall
localrepo: convert various repo.set() users to repo.revs()
r15404 base, dest, base)
Matt Mackall
rebase: simplify set generation
r15271
Pierre-Yves David
phases: prevent rebase to rebase immutable changeset.
r15742 if rebaseset:
root = min(rebaseset)
else:
root = None
Matt Mackall
rebase: simplify set generation
r15271
if not rebaseset:
Patrick Mezard
rebase: add missing EOL to debug strings
r16565 repo.ui.debug('base is ancestor of destination\n')
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 result = None
Matt Mackall
localrepo: convert various repo.set() users to repo.revs()
r15404 elif not keepf and list(repo.revs('first(children(%ld) - %ld)',
rebaseset, rebaseset)):
Matt Mackall
rebase: simplify check for orphaned descendants
r15272 raise util.Abort(
_("can't remove original changesets with"
" unrebased descendants"),
hint=_('use --keep to keep original changesets'))
Pierre-Yves David
phases: prevent rebase to rebase immutable changeset.
r15742 elif not keepf and not repo[root].mutable():
Wagner Bruna
rebase: drop uppercase in abort message
r15945 raise util.Abort(_("can't rebase immutable changeset %s")
Pierre-Yves David
phases: prevent rebase to rebase immutable changeset.
r15742 % repo[root],
hint=_('see hg help phases for details'))
Matt Mackall
rebase: simplify check for orphaned descendants
r15272 else:
Pierre-Yves David
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
r17005 result = buildstate(repo, dest, rebaseset, collapsef)
Matt Mackall
rebase: simplify check for orphaned descendants
r15272
Stefano Tortarolo
rebase: refactoring...
r10351 if not result:
# Empty state built, nothing to rebase
Martin Geisler
use ui instead of repo.ui when the former is in scope
r8615 ui.status(_('nothing to rebase\n'))
Matt Mackall
rebase: add error codes...
r11205 return 1
Stefano Tortarolo
rebase: refactoring...
r10351 else:
originalwd, target, state = result
if collapsef:
Bryan O'Sullivan
revlog: ancestors(*revs) becomes ancestors(revs) (API)...
r16866 targetancestors = set(repo.changelog.ancestors([target]))
Stefano Tortarolo
rebase: ensure target is not taken as external (issue3085)...
r15469 targetancestors.add(target)
Stefano Tortarolo
rebase: refactoring...
r10351 external = checkexternal(repo, state, targetancestors)
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 if keepbranchesf:
Martin Geisler
rebase: don't use util.Abort for an internal error
r13609 assert not extrafn, 'cannot use both keepbranches and extrafn'
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 def extrafn(ctx, extra):
extra['branch'] = ctx.branch()
Stefano Tortarolo
rebase: block collapse with keepbranches on multiple named branches (issue2112)...
r14897 if collapsef:
branches = set()
for rev in state:
branches.add(repo[rev].branch())
if len(branches) > 1:
Augie Fackler
rebase: remove trailing whitespace found by check-code
r14917 raise util.Abort(_('cannot collapse multiple named '
Stefano Tortarolo
rebase: block collapse with keepbranches on multiple named branches (issue2112)...
r14897 'branches'))
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952
Stefano Tortarolo
Add rebase extension
r6906 # Rebase
Stefano Tortarolo
rebase: refactoring...
r10351 if not targetancestors:
Bryan O'Sullivan
revlog: ancestors(*revs) becomes ancestors(revs) (API)...
r16866 targetancestors = set(repo.changelog.ancestors([target]))
Stefano Tortarolo
rebase: refactoring...
r10351 targetancestors.add(target)
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884 # Keep track of the current bookmarks in order to reset them later
currentbookmarks = repo._bookmarks.copy()
timeless
rebase/progress: Adding progress for rebasing
r11729 sortedstate = sorted(state)
total = len(sortedstate)
pos = 0
for rev in sortedstate:
pos += 1
Stefano Tortarolo
Add rebase extension
r6906 if state[rev] == -1:
Alecs King
minor style fix: hgext/rebase.py:157 -- line too long...
r11761 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
timeless
progress: dropping superfluous space from units
r12744 _('changesets'), total)
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 storestatus(repo, originalwd, target, state, collapsef, keepf,
keepbranchesf, external)
Stefano Tortarolo
rebase: refactoring...
r10351 p1, p2 = defineparents(repo, rev, target, state,
targetancestors)
if len(repo.parents()) == 2:
repo.ui.debug('resuming interrupted rebase\n')
else:
Stefano Tortarolo
rebase: add --tool argument for specifying merge tool
r13856 try:
ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
Patrick Mezard
rebase: allow collapsing branches in place (issue3111)...
r16696 stats = rebasenode(repo, rev, p1, state, collapsef)
Stefano Tortarolo
rebase: add --tool argument for specifying merge tool
r13856 if stats and stats[3] > 0:
raise util.Abort(_('unresolved conflicts (see hg '
'resolve, then hg rebase --continue)'))
finally:
ui.setconfig('ui', 'forcemerge', '')
Matt Mackall
cmdutil: simplify duplicatecopies
r15777 cmdutil.duplicatecopies(repo, rev, target)
Stefano Tortarolo
rebase: refactoring...
r10351 if not collapsef:
Matt Mackall
rebase: add --edit switch
r15219 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn,
editor=editor)
Stefano Tortarolo
rebase: refactoring...
r10351 else:
# Skip commit if we are collapsing
Patrick Mezard
localrepo: add setparents() to adjust dirstate copies (issue3407)...
r16551 repo.setparents(repo[p1].node())
Stefano Tortarolo
rebase: refactoring...
r10351 newrev = None
# Update the state
if newrev is not None:
state[rev] = repo[newrev].rev()
else:
if not collapsef:
ui.note(_('no changes, revision %d skipped\n') % rev)
ui.debug('next revision set to %s\n' % p1)
skipped.add(rev)
state[rev] = p1
timeless
rebase/progress: Adding progress for rebasing
r11729 ui.progress(_('rebasing'), None)
Stefano Tortarolo
Add rebase extension
r6906 ui.note(_('rebase merging completed\n'))
Stefano Tortarolo
rebase: add option to not commit after a collapsing...
r10677 if collapsef and not keepopen:
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923 p1, p2 = defineparents(repo, min(state), target,
Stefano Tortarolo
Add rebase extension
r6906 state, targetancestors)
Radomir Dopieralski
rebase: add -m/--message to rebase --collapse (issue2389)...
r13661 if collapsemsg:
commitmsg = collapsemsg
else:
commitmsg = 'Collapsed revision'
for rebased in state:
if rebased not in skipped and state[rebased] != nullmerge:
commitmsg += '\n* %s' % repo[rebased].description()
commitmsg = ui.edit(commitmsg, repo.ui.username())
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
Matt Mackall
rebase: add --edit switch
r15219 extrafn=extrafn, editor=editor)
Stefano Tortarolo
Add rebase extension
r6906
if 'qtip' in repo.tags():
updatemq(repo, state, skipped, **opts)
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884 if currentbookmarks:
# Nodeids are needed to reset bookmarks
nstate = {}
for k, v in state.iteritems():
if v != nullmerge:
nstate[repo[k].node()] = repo[v].node()
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 if not keepf:
Stefano Tortarolo
Add rebase extension
r6906 # Remove no more useful revisions
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 rebased = [rev for rev in state if state[rev] != nullmerge]
if rebased:
Bryan O'Sullivan
revlog: descendants(*revs) becomes descendants(revs) (API)...
r16867 if set(repo.changelog.descendants([min(rebased)])) - set(state):
Stefano Tortarolo
rebase: split line longer than 80 chars
r10436 ui.warn(_("warning: new changesets detected "
"on source branch, not stripping\n"))
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 else:
Matt Mackall
Fix up rebase's handling of strip backups
r11201 # backup the old csets by default
repair.strip(ui, repo, repo[min(rebased)].node(), "all")
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884 if currentbookmarks:
updatebookmarks(repo, nstate, currentbookmarks, **opts)
Stefano Tortarolo
Add rebase extension
r6906 clearstatus(repo)
Matt Mackall
rebase: only show "rebase completed" message with -v
r11203 ui.note(_("rebase completed\n"))
Stefano Tortarolo
rebase: disable rollback after rebasing
r7130 if os.path.exists(repo.sjoin('undo')):
Adrian Buehlmann
rename util.unlink to unlinkpath
r13235 util.unlinkpath(repo.sjoin('undo'))
Stefano Tortarolo
Add rebase extension
r6906 if skipped:
ui.note(_("%d revisions have been skipped\n") % len(skipped))
finally:
Ronny Pfannschmidt
switch lock releasing in the extensions from gc to explicit
r8112 release(lock, wlock)
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: refactoring...
r10351 def checkexternal(repo, state, targetancestors):
"""Check whether one or more external revisions need to be taken in
consideration. In the latter case, abort.
"""
external = nullrev
source = min(state)
for rev in state:
if rev == source:
continue
# Check externals and fail if there are more than one
for p in repo[rev].parents():
if (p.rev() not in state
and p.rev() not in targetancestors):
if external != nullrev:
raise util.Abort(_('unable to collapse, there is more '
'than one external parent'))
external = p.rev()
return external
Matt Mackall
rebase: add --edit switch
r15219 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None):
Stefano Tortarolo
rebase: refactoring...
r10351 'Commit the changes and store useful information in extra'
Stefano Tortarolo
Add rebase extension
r6906 try:
Patrick Mezard
localrepo: add setparents() to adjust dirstate copies (issue3407)...
r16551 repo.setparents(repo[p1].node(), repo[p2].node())
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 ctx = repo[rev]
Stefano Tortarolo
rebase: refactoring...
r10351 if commitmsg is None:
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 commitmsg = ctx.description()
Patrick Mezard
rebase: fix --collapse with --keepbranches (issue2100)...
r10762 extra = {'rebase_source': ctx.hex()}
if extrafn:
extrafn(ctx, extra)
Stefano Tortarolo
Add rebase extension
r6906 # Commit might fail if unresolved files exist
Patrick Mezard
rebase: fix --collapse with --keepbranches (issue2100)...
r10762 newrev = repo.commit(text=commitmsg, user=ctx.user(),
Matt Mackall
rebase: add --edit switch
r15219 date=ctx.date(), extra=extra, editor=editor)
Patrick Mezard
rebase: fix bug where --keepbranches could leave wrong branch in dirstate...
r8266 repo.dirstate.setbranch(repo[newrev].branch())
Alain Leufroy <alain.leufroyATgmailMYDOTcom>
rebase: fix phases movement...
r15917 targetphase = max(ctx.phase(), phases.draft)
# retractboundary doesn't overwrite upper phase inherited from parent
newnode = repo[newrev].node()
Matt Mackall
rebase: only advance phase on successful commit
r15923 if newnode:
phases.retractboundary(repo, targetphase, [newnode])
Stefano Tortarolo
Add rebase extension
r6906 return newrev
except util.Abort:
# Invalidate the previous setparents
repo.dirstate.invalidate()
raise
Patrick Mezard
rebase: allow collapsing branches in place (issue3111)...
r16696 def rebasenode(repo, rev, p1, state, collapse):
Stefano Tortarolo
Add rebase extension
r6906 'Rebase a single revision'
# Merge phase
Stefano Tortarolo
rebase: refactoring...
r10351 # Update to target and merge it with local
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)
Stefano Tortarolo
Add rebase extension
r6906 else:
Stefano Tortarolo
rebase: refactoring...
r10351 repo.ui.debug(" already in target\n")
repo.dirstate.write()
repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
Matt Mackall
rebase: use merge's ancestor parameter
r13875 base = None
if repo[rev].rev() != repo[min(state)].rev():
Matt Mackall
misc: replace .parents()[0] with p1()
r13878 base = repo[rev].p1().node()
Patrick Mezard
rebase: allow collapsing branches in place (issue3111)...
r16696 # 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)
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923
Stefano Tortarolo
Add rebase extension
r6906 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
P1n = parents[0].rev()
if P1n in targetancestors:
p1 = target
elif P1n in state:
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 if state[P1n] == nullmerge:
p1 = target
else:
p1 = state[P1n]
Stefano Tortarolo
Add rebase extension
r6906 else: # P1n external
p1 = target
p2 = P1n
if len(parents) == 2 and parents[1].rev() not in targetancestors:
P2n = parents[1].rev()
# interesting second parent
if P2n in state:
if p1 == target: # P1n in targetancestors or external
p1 = state[P2n]
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 '
'would have 3 parents') % rev)
p2 = P2n
Stefano Tortarolo
rebase: refactoring...
r10351 repo.ui.debug(" future parents are %d and %d\n" %
(repo[p1].rev(), repo[p2].rev()))
Stefano Tortarolo
Add rebase extension
r6906 return p1, p2
Stefano Tortarolo
rebase: keep original mq patch format (Issue1574)...
r7955 def isagitpatch(repo, patchname):
'Return true if the given patch is in git format'
mqpatch = os.path.join(repo.mq.path, patchname)
for line in patch.linereader(file(mqpatch, 'rb')):
if line.startswith('diff --git'):
return True
return False
Stefano Tortarolo
Add rebase extension
r6906 def updatemq(repo, state, skipped, **opts):
'Update rebased mq patches - finalize and then import them'
mqrebase = {}
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 mq = repo.mq
Adrian Buehlmann
mq: rename full_series to fullseries
r14572 original_series = mq.fullseries[:]
Patrick Mezard
rebase: preserve mq series order, guarded patches (issue2849)...
r16531 skippedpatches = set()
Stefano Tortarolo
rebase: restore mq guards after rebasing (issue2107)...
r14497
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 for p in mq.applied:
rev = repo[p.node].rev()
if rev in state:
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 (rev, p.name))
mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
Patrick Mezard
rebase: preserve mq series order, guarded patches (issue2849)...
r16531 else:
# Applied but not rebased, not sure this should happen
skippedpatches.add(p.name)
Stefano Tortarolo
Add rebase extension
r6906
if mqrebase:
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 mq.finish(repo, mqrebase.keys())
Stefano Tortarolo
Add rebase extension
r6906
# We must start import from the newest revision
Matt Mackall
replace various uses of list.reverse()
r8210 for rev in sorted(mqrebase, reverse=True):
Stefano Tortarolo
Add rebase extension
r6906 if rev not in skipped:
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 name, isgit = mqrebase[rev]
repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
mq.qimport(repo, (), patchname=name, git=isgit,
rev=[str(state[rev])])
Patrick Mezard
rebase: preserve mq series order, guarded patches (issue2849)...
r16531 else:
# Rebased and skipped
skippedpatches.add(mqrebase[rev][0])
Stefano Tortarolo
rebase: restore mq guards after rebasing (issue2107)...
r14497
Patrick Mezard
rebase: preserve mq series order, guarded patches (issue2849)...
r16531 # Patches were either applied and rebased and imported in
# order, applied and removed or unapplied. Discard the removed
# ones while preserving the original series order and guards.
newseries = [s for s in original_series
if mq.guard_re.split(s, 1)[0] not in skippedpatches]
mq.fullseries[:] = newseries
mq.seriesdirty = True
Adrian Buehlmann
mq: rename save_dirty to savedirty
r14580 mq.savedirty()
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884 def updatebookmarks(repo, nstate, originalbookmarks, **opts):
'Move bookmarks to their correct changesets'
current = repo._bookmarkcurrent
for k, v in originalbookmarks.iteritems():
if v in nstate:
if nstate[v] != nullmerge:
# reset the pointer if the bookmark was moved incorrectly
if k != current:
repo._bookmarks[k] = nstate[v]
bookmarks.write(repo)
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
external):
Stefano Tortarolo
Add rebase extension
r6906 'Store the current status to allow recovery'
f = repo.opener("rebasestate", "w")
f.write(repo[originalwd].hex() + '\n')
f.write(repo[target].hex() + '\n')
f.write(repo[external].hex() + '\n')
f.write('%d\n' % int(collapse))
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 f.write('%d\n' % int(keep))
f.write('%d\n' % int(keepbranches))
Dirkjan Ochtman
use dict.iteritems() rather than dict.items()...
r7622 for d, v in state.iteritems():
Stefano Tortarolo
Add rebase extension
r6906 oldrev = repo[d].hex()
Stefano Tortarolo
rebase: treat nullmerge as a special case in rebasestate (issue3046)...
r15464 if v != nullmerge:
newrev = repo[v].hex()
else:
newrev = v
Stefano Tortarolo
Add rebase extension
r6906 f.write("%s:%s\n" % (oldrev, newrev))
f.close()
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug('rebase status stored\n')
Stefano Tortarolo
Add rebase extension
r6906
def clearstatus(repo):
'Remove the status files'
if os.path.exists(repo.join("rebasestate")):
Adrian Buehlmann
rename util.unlink to unlinkpath
r13235 util.unlinkpath(repo.join("rebasestate"))
Stefano Tortarolo
Add rebase extension
r6906
def restorestatus(repo):
'Restore a previously stored status'
try:
target = None
collapse = False
external = nullrev
state = {}
f = repo.opener("rebasestate")
for i, l in enumerate(f.read().splitlines()):
if i == 0:
originalwd = repo[l].rev()
elif i == 1:
target = repo[l].rev()
elif i == 2:
external = repo[l].rev()
elif i == 3:
collapse = bool(int(l))
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 elif i == 4:
keep = bool(int(l))
elif i == 5:
keepbranches = bool(int(l))
Stefano Tortarolo
Add rebase extension
r6906 else:
oldrev, newrev = l.split(':')
Stefano Tortarolo
rebase: treat nullmerge as a special case in rebasestate (issue3046)...
r15464 if newrev != str(nullmerge):
state[repo[oldrev].rev()] = repo[newrev].rev()
else:
state[repo[oldrev].rev()] = int(newrev)
Benoit Boissinot
rebase: recompute the set of skipped rev when using --continue (issue2330)
r11843 skipped = set()
# recompute the set of skipped revs
if not collapse:
seen = set([target])
for old, new in sorted(state.items()):
if new != nullrev and new in seen:
skipped.add(old)
seen.add(new)
repo.ui.debug('computed skipped revs: %s\n' % skipped)
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug('rebase status resumed\n')
Benoit Boissinot
rebase: recompute the set of skipped rev when using --continue (issue2330)
r11843 return (originalwd, target, state, skipped,
collapse, keep, keepbranches, external)
Stefano Tortarolo
Add rebase extension
r6906 except IOError, err:
if err.errno != errno.ENOENT:
raise
raise util.Abort(_('no rebase in progress'))
def abort(repo, originalwd, target, state):
'Restore the repository to its original state'
Matt Mackall
rebase: properly calculate descendant set when aborting (issue3332)...
r16280 dstates = [s for s in state.values() if s != nullrev]
if [d for d in dstates if not repo[d].mutable()]:
Alain Leufroy <alain.leufroyATgmailMYDOTcom>
rebase: fix phases movement...
r15917 repo.ui.warn(_("warning: immutable rebased changeset detected, "
"can't abort\n"))
return -1
Matt Mackall
rebase: properly calculate descendant set when aborting (issue3332)...
r16280
descendants = set()
if dstates:
Bryan O'Sullivan
revlog: descendants(*revs) becomes descendants(revs) (API)...
r16867 descendants = set(repo.changelog.descendants(dstates))
Matt Mackall
rebase: properly calculate descendant set when aborting (issue3332)...
r16280 if descendants - set(dstates):
Stefano Tortarolo
Add rebase extension
r6906 repo.ui.warn(_("warning: new changesets detected on target branch, "
Matt Mackall
rebase: properly calculate descendant set when aborting (issue3332)...
r16280 "can't abort\n"))
Matt Mackall
rebase: add error codes...
r11205 return -1
Stefano Tortarolo
Add rebase extension
r6906 else:
# Strip from the first rebased revision
merge.update(repo, repo[originalwd].rev(), False, True, False)
Stefano Tortarolo
rebase: --abort doesn't strip away the target changeset (issue2220)...
r11316 rebased = filter(lambda x: x > -1 and x != target, state.values())
Stefano Tortarolo
Add rebase extension
r6906 if rebased:
strippoint = min(rebased)
Matt Mackall
Fix up rebase's handling of strip backups
r11201 # no backup of rebased cset versions needed
repair.strip(repo.ui, repo, repo[strippoint].node())
Stefano Tortarolo
Add rebase extension
r6906 clearstatus(repo)
timeless
rebase: abort message should appear even with --quiet
r12861 repo.ui.warn(_('rebase aborted\n'))
Matt Mackall
rebase: add error codes...
r11205 return 0
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
r17005 def buildstate(repo, dest, rebaseset, collapse):
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 '''Define which revisions are going to be rebased and where
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 repo: repo
dest: context
rebaseset: set of rev
Pierre-Yves David
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
r17005 '''
Stefano Tortarolo
Add rebase extension
r6906
Greg Ward
rebase: always check if rebasing onto an applied mq patch....
r10672 # This check isn't strictly necessary, since mq detects commits over an
# applied patch. But it prevents messing up the working directory when
# a partially completed rebase is blocked by mq.
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 if 'qtip' in repo.tags() and (dest.node() in
Benoit Boissinot
mq: avoid many hex/bin conversions, keep the binary node when possible
r10678 [s.node for s in repo.mq.applied]):
Greg Ward
rebase: always check if rebasing onto an applied mq patch....
r10672 raise util.Abort(_('cannot rebase onto an applied mq patch'))
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 roots = list(repo.set('roots(%ld)', rebaseset))
if not roots:
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 raise util.Abort(_('no matching revisions'))
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 if len(roots) > 1:
Pierre-Yves David
rebase: add --rev option to rebase...
r15270 raise util.Abort(_("can't rebase multiple roots"))
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 root = roots[0]
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 commonbase = root.ancestor(dest)
if commonbase == root:
raise util.Abort(_('source is ancestor of destination'))
if commonbase == dest:
samebranch = root.branch() == dest.branch()
Patrick Mezard
rebase: allow collapsing branches in place (issue3111)...
r16696 if not collapse and samebranch and root in dest.children():
repo.ui.debug('source is a child of destination\n')
return None
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root))
state = dict.fromkeys(rebaseset, nullrev)
Pierre-Yves David
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
r17005 # Rebase tries to turn <dest> into a parent of <root> while
# preserving the number of parents of rebased changesets:
#
# - A changeset with a single parent will always be rebased as a
# changeset with a single parent.
#
# - A merge will be rebased as merge unless its parents are both
# ancestors of <dest> or are themselves in the rebased set and
# pruned while rebased.
#
# If one parent of <root> is an ancestor of <dest>, the rebased
# version of this parent will be <dest>. This is always true with
# --base option.
#
# Otherwise, we need to *replace* the original parents with
# <dest>. This "detaches" the rebased set from its former location
# and rebases it onto <dest>. Changes introduced by ancestors of
# <root> not common with <dest> (the detachset, marked as
# nullmerge) are "removed" from the rebased changesets.
#
# - If <root> has a single parent, set it to <dest>.
#
# - If <root> is a merge, we cannot decide which parent to
# replace, the rebase operation is not clearly defined.
#
# The table below sums up this behavior:
#
# +--------------------+----------------------+-------------------------+
# | | one parent | merge |
# +--------------------+----------------------+-------------------------+
# | parent in ::<dest> | new parent is <dest> | parents in ::<dest> are |
# | | | remapped to <dest> |
# +--------------------+----------------------+-------------------------+
# | unrelated source | new parent is <dest> | ambiguous, abort |
# +--------------------+----------------------+-------------------------+
#
# The actual abort is handled by `defineparents`
if len(root.parents()) <= 1:
# (strict) ancestors of <root> not ancestors of <dest>
detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root)
state.update(dict.fromkeys(detachset, nullmerge))
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 return repo['.'].rev(), dest.rev(), state
Stefano Tortarolo
Add rebase extension
r6906
Matt Mackall
extensions: use new wrapper functions
r7216 def pullrebase(orig, ui, repo, *args, **opts):
Stefano Tortarolo
Add rebase extension
r6906 'Call rebase after pull if the latter has been invoked with --rebase'
if opts.get('rebase'):
if opts.get('update'):
Martijn Pieters
Fix typeerror when specifying both --rebase and --pull
r8242 del opts['update']
Martin Geisler
do not attempt to translate ui.debug output
r9467 ui.debug('--update and --rebase are not compatible, ignoring '
'the update flag\n')
Stefano Tortarolo
Add rebase extension
r6906
Matt Mackall
rebase: move bookmarks as needed with pull --rebase (issue3285)
r16228 movemarkfrom = repo['.'].node()
Matt Mackall
cmdutil: bail_if_changed to bailifchanged
r14289 cmdutil.bailifchanged(repo)
Stefano Tortarolo
Add rebase extension
r6906 revsprepull = len(repo)
Sune Foldager
rebase: improve output of hg pull --rebase (issue2072)
r10628 origpostincoming = commands.postincoming
def _dummy(*args, **kwargs):
pass
commands.postincoming = _dummy
try:
orig(ui, repo, *args, **opts)
finally:
commands.postincoming = origpostincoming
Stefano Tortarolo
Add rebase extension
r6906 revspostpull = len(repo)
if revspostpull > revsprepull:
Matt Mackall
extensions: use new wrapper functions
r7216 rebase(ui, repo, **opts)
Stefano Tortarolo
rebase: pull --rebase updates if there is nothing to rebase
r7786 branch = repo[None].branch()
dest = repo[branch].rev()
if dest != repo['.'].rev():
# there was nothing to rebase we force an update
Sune Foldager
rebase: improve output of hg pull --rebase (issue2072)
r10628 hg.update(repo, dest)
Matt Mackall
rebase: move bookmarks as needed with pull --rebase (issue3285)
r16228 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
ui.status(_("updating bookmark %s\n")
% repo._bookmarkcurrent)
Stefano Tortarolo
Add rebase extension
r6906 else:
Adrian Buehlmann
rebase: add option --tool/-t for 'pull --rebase'...
r14444 if opts.get('tool'):
raise util.Abort(_('--tool can only be used with --rebase'))
Matt Mackall
extensions: use new wrapper functions
r7216 orig(ui, repo, *args, **opts)
Stefano Tortarolo
Add rebase extension
r6906
def uisetup(ui):
'Replace pull with a decorator to provide --rebase option'
Matt Mackall
extensions: use new wrapper functions
r7216 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
entry[1].append(('', 'rebase', None,
Adrian Buehlmann
rebase: add option --tool/-t for 'pull --rebase'...
r14444 _("rebase working directory to branch head")))
entry[1].append(('t', 'tool', '',
_("specify merge tool for rebase")))