##// END OF EJS Templates
rebase: always check if rebasing onto an applied mq patch....
rebase: always check if rebasing onto an applied mq patch. Previously, it only checked for an mq patch if the user explicitly passed -d/--dest. But rebasing onto an mq patch is a bad idea regardless of how we determine the rebase destination.

File last commit:

r10672:c2e1e637 stable
r10672:c2e1e637 stable
Show More
rebase.py
542 lines | 21.1 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 '''
Matt Mackall
error: move ParseError
r7636 from mercurial import util, repair, merge, cmdutil, commands, error
Stefano Tortarolo
rebase: keep original mq patch format (Issue1574)...
r7955 from mercurial import extensions, ancestor, copies, patch
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
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
useful for linearizing local changes relative to a master
development tree.
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.
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
lock = wlock = None
try:
lock = repo.lock()
wlock = repo.wlock()
# Validate input and define rebasing points
destf = opts.get('dest', None)
srcf = opts.get('source', None)
basef = opts.get('base', None)
contf = opts.get('continue')
abortf = opts.get('abort')
collapsef = opts.get('collapse', False)
Augie Fackler
rebase: add support to keep branch names...
r7468 extrafn = opts.get('extrafn')
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 keepf = opts.get('keep', False)
keepbranchesf = opts.get('keepbranches', False)
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 detachf = opts.get('detach', False)
Augie Fackler
rebase: add support to keep branch names...
r7468
Stefano Tortarolo
Add rebase extension
r6906 if contf or abortf:
if contf and abortf:
Matt Mackall
error: move ParseError
r7636 raise error.ParseError('rebase',
_('cannot use both abort and continue'))
Stefano Tortarolo
Add rebase extension
r6906 if collapsef:
Matt Mackall
error: move ParseError
r7636 raise error.ParseError(
'rebase', _('cannot use collapse with continue or abort'))
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 if detachf:
raise error.ParseError(
'rebase', _('cannot use detach with continue or abort'))
Martin Geisler
remove unnecessary outer parenthesis in if-statements
r8117 if srcf or basef or destf:
Matt Mackall
error: move ParseError
r7636 raise error.ParseError('rebase',
Stefano Tortarolo
Add rebase extension
r6906 _('abort and continue do not allow specifying revisions'))
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 (originalwd, target, state, collapsef, keepf,
keepbranchesf, external) = restorestatus(repo)
Stefano Tortarolo
Add rebase extension
r6906 if abortf:
abort(repo, originalwd, target, state)
return
else:
if srcf and basef:
Matt Mackall
error: move ParseError
r7636 raise error.ParseError('rebase', _('cannot specify both a '
'revision and a base'))
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 if detachf:
if not srcf:
raise error.ParseError(
'rebase', _('detach requires a revision to be specified'))
if basef:
raise error.ParseError(
'rebase', _('cannot specify a base with detach'))
Stefano Tortarolo
Add rebase extension
r6906 cmdutil.bail_if_changed(repo)
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 result = buildstate(repo, destf, srcf, basef, detachf)
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'))
Stefano Tortarolo
Add rebase extension
r6906 return
Stefano Tortarolo
rebase: refactoring...
r10351 else:
originalwd, target, state = result
if collapsef:
targetancestors = set(repo.changelog.ancestors(target))
external = checkexternal(repo, state, targetancestors)
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 if keepbranchesf:
if extrafn:
raise error.ParseError(
'rebase', _('cannot use both keepbranches and extrafn'))
def extrafn(ctx, extra):
extra['branch'] = ctx.branch()
Stefano Tortarolo
Add rebase extension
r6906 # Rebase
Stefano Tortarolo
rebase: refactoring...
r10351 if not targetancestors:
targetancestors = set(repo.changelog.ancestors(target))
targetancestors.add(target)
Stefano Tortarolo
Add rebase extension
r6906
Matt Mackall
replace util.sort with sorted built-in...
r8209 for rev in sorted(state):
Stefano Tortarolo
Add rebase extension
r6906 if state[rev] == -1:
Stefano Tortarolo
rebase: refactoring...
r10351 ui.debug("rebasing %d:%s\n" % (rev, repo[rev]))
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:
stats = rebasenode(repo, rev, p1, p2, state)
if stats and stats[3] > 0:
raise util.Abort(_('fix unresolved conflicts with hg '
'resolve then run hg rebase --continue'))
updatedirstate(repo, rev, target, p2)
if not collapsef:
extra = {'rebase_source': repo[rev].hex()}
if extrafn:
extrafn(repo[rev], extra)
newrev = concludenode(repo, rev, p1, p2, extra=extra)
else:
# Skip commit if we are collapsing
repo.dirstate.setparents(repo[p1].node())
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
Stefano Tortarolo
Add rebase extension
r6906 ui.note(_('rebase merging completed\n'))
if collapsef:
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)
Stefano Tortarolo
rebase: refactoring...
r10351 commitmsg = 'Collapsed revision'
for rebased in state:
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 if rebased not in skipped and state[rebased] != nullmerge:
Stefano Tortarolo
rebase: refactoring...
r10351 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,
Stefano Tortarolo
rebase: refactoring...
r10351 extra=extrafn)
Stefano Tortarolo
Add rebase extension
r6906
if 'qtip' in repo.tags():
updatemq(repo, state, skipped, **opts)
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:
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:
repair.strip(ui, repo, repo[min(rebased)].node(), "strip")
Stefano Tortarolo
Add rebase extension
r6906
clearstatus(repo)
ui.status(_("rebase completed\n"))
Stefano Tortarolo
rebase: disable rollback after rebasing
r7130 if os.path.exists(repo.sjoin('undo')):
util.unlink(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 rebasemerge(repo, rev, first=False):
'return the correct ancestor'
oldancestor = ancestor.ancestor
def newancestor(a, b, pfunc):
if b == rev:
return repo[rev].parents()[0].rev()
return oldancestor(a, b, pfunc)
Stefano Tortarolo
Add rebase extension
r6906
Stefano Tortarolo
rebase: refactoring...
r10351 if not first:
ancestor.ancestor = newancestor
else:
repo.ui.debug("first revision, do not change ancestor\n")
try:
stats = merge.update(repo, rev, True, True, False)
return stats
finally:
ancestor.ancestor = oldancestor
Benoit Boissinot
rebase: use set instead of dict
r8454
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
def updatedirstate(repo, rev, p1, p2):
"""Keep track of renamed files in the revision that is going to be rebased
"""
# Here we simulate the copies and renames in the source changeset
cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
m1 = repo[rev].manifest()
m2 = repo[p1].manifest()
for k, v in cop.iteritems():
if k in m1:
if v in m1 or v in m2:
repo.dirstate.copy(v, k)
if v in m2 and v not in m1:
repo.dirstate.remove(v)
def concludenode(repo, rev, p1, p2, commitmsg=None, extra=None):
'Commit the changes and store useful information in extra'
Stefano Tortarolo
Add rebase extension
r6906 try:
Stefano Tortarolo
rebase: refactoring...
r10351 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
if commitmsg is None:
Stefano Tortarolo
Add rebase extension
r6906 commitmsg = repo[rev].description()
Stefano Tortarolo
rebase: refactoring...
r10351 if extra is None:
extra = {}
Stefano Tortarolo
Add rebase extension
r6906 # Commit might fail if unresolved files exist
Matt Mackall
commit: drop the now-unused files parameter
r8706 newrev = repo.commit(text=commitmsg, user=repo[rev].user(),
date=repo[rev].date(), extra=extra)
Patrick Mezard
rebase: fix bug where --keepbranches could leave wrong branch in dirstate...
r8266 repo.dirstate.setbranch(repo[newrev].branch())
Stefano Tortarolo
Add rebase extension
r6906 return newrev
except util.Abort:
# Invalidate the previous setparents
repo.dirstate.invalidate()
raise
Stefano Tortarolo
rebase: refactoring...
r10351 def rebasenode(repo, rev, p1, p2, state):
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]))
first = repo[rev].rev() == repo[min(state)].rev()
stats = rebasemerge(repo, rev, first)
return stats
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 = {}
for p in repo.mq.applied:
if repo[p.rev].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' %
Stefano Tortarolo
Add rebase extension
r6906 (repo[p.rev].rev(), p.name))
Stefano Tortarolo
rebase: keep original mq patch format (Issue1574)...
r7955 mqrebase[repo[p.rev].rev()] = (p.name, isagitpatch(repo, p.name))
Stefano Tortarolo
Add rebase extension
r6906
if mqrebase:
repo.mq.finish(repo, mqrebase.keys())
# 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:
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug('import mq patch %d (%s)\n'
Stefano Tortarolo
rebase: keep original mq patch format (Issue1574)...
r7955 % (state[rev], mqrebase[rev][0]))
repo.mq.qimport(repo, (), patchname=mqrebase[rev][0],
git=mqrebase[rev][1],rev=[str(state[rev])])
Stefano Tortarolo
Add rebase extension
r6906 repo.mq.save_dirty()
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()
newrev = repo[v].hex()
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")):
util.unlink(repo.join("rebasestate"))
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(':')
state[repo[oldrev].rev()] = repo[newrev].rev()
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug('rebase status resumed\n')
Stefano Tortarolo
rebase: store/restore arguments correctly...
r7952 return originalwd, target, state, 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'
Martin Geisler
util: use built-in set and frozenset...
r8150 if set(repo.changelog.descendants(target)) - set(state.values()):
Stefano Tortarolo
Add rebase extension
r6906 repo.ui.warn(_("warning: new changesets detected on target branch, "
"not stripping\n"))
else:
# Strip from the first rebased revision
merge.update(repo, repo[originalwd].rev(), False, True, False)
rebased = filter(lambda x: x > -1, state.values())
if rebased:
strippoint = min(rebased)
repair.strip(repo.ui, repo, repo[strippoint].node(), "strip")
clearstatus(repo)
repo.ui.status(_('rebase aborted\n'))
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 def buildstate(repo, dest, src, base, detach):
Stefano Tortarolo
Add rebase extension
r6906 'Define which revisions are going to be rebased and where'
Martin Geisler
util: use built-in set and frozenset...
r8150 targetancestors = set()
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 detachset = set()
Stefano Tortarolo
Add rebase extension
r6906
if not dest:
Peter Arrenbrecht
cleanup: whitespace cleanup
r7877 # Destination defaults to the latest revision in the current branch
Stefano Tortarolo
Add rebase extension
r6906 branch = repo[None].branch()
dest = repo[branch].rev()
else:
dest = repo[dest].rev()
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.
if 'qtip' in repo.tags() and (repo[dest].hex() in
[s.rev for s in repo.mq.applied]):
raise util.Abort(_('cannot rebase onto an applied mq patch'))
Stefano Tortarolo
Add rebase extension
r6906 if src:
commonbase = repo[src].ancestor(repo[dest])
if commonbase == repo[src]:
Sune Foldager
rebase: improve error and debug messages
r9577 raise util.Abort(_('source is ancestor of destination'))
Stefano Tortarolo
Add rebase extension
r6906 if commonbase == repo[dest]:
Sune Foldager
rebase: improve error and debug messages
r9577 raise util.Abort(_('source is descendant of destination'))
Stefano Tortarolo
Add rebase extension
r6906 source = repo[src].rev()
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 if detach:
# We need to keep track of source's ancestors up to the common base
srcancestors = set(repo.changelog.ancestors(source))
baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
detachset = srcancestors - baseancestors
detachset.remove(commonbase.rev())
Stefano Tortarolo
Add rebase extension
r6906 else:
if base:
cwd = repo[base].rev()
else:
cwd = repo['.'].rev()
if cwd == dest:
Sune Foldager
rebase: improve error and debug messages
r9577 repo.ui.debug('source and destination are the same\n')
Stefano Tortarolo
Add rebase extension
r6906 return None
Martin Geisler
util: use built-in set and frozenset...
r8150 targetancestors = set(repo.changelog.ancestors(dest))
Stefano Tortarolo
Add rebase extension
r6906 if cwd in targetancestors:
Sune Foldager
rebase: improve error and debug messages
r9577 repo.ui.debug('source is ancestor of destination\n')
Stefano Tortarolo
Add rebase extension
r6906 return None
Martin Geisler
util: use built-in set and frozenset...
r8150 cwdancestors = set(repo.changelog.ancestors(cwd))
Sune Foldager
rebase: return early when source is descendant of destination...
r9578 if dest in cwdancestors:
repo.ui.debug('source is descendant of destination\n')
return None
Stefano Tortarolo
Add rebase extension
r6906 cwdancestors.add(cwd)
rebasingbranch = cwdancestors - targetancestors
source = min(rebasingbranch)
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
Stefano Tortarolo
Add rebase extension
r6906 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 state.update(dict.fromkeys(detachset, nullmerge))
Stefano Tortarolo
Add rebase extension
r6906 state[source] = nullrev
Stefano Tortarolo
rebase: refactoring...
r10351 return repo['.'].rev(), repo[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
cmdutil.bail_if_changed(repo)
revsprepull = len(repo)
Matt Mackall
extensions: use new wrapper functions
r7216 orig(ui, repo, *args, **opts)
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
merge.update(repo, dest, False, False, False)
Stefano Tortarolo
Add rebase extension
r6906 else:
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,
_("rebase working directory to branch head"))
)
Stefano Tortarolo
Add rebase extension
r6906
cmdtable = {
"rebase":
(rebase,
[
Greg Ward
rebase: improve help text...
r10646 ('s', 'source', '', _('rebase from the specified changeset')),
('b', 'base', '', _('rebase from the base of the specified changeset '
'(up to greatest common ancestor of base and dest)')),
('d', 'dest', '', _('rebase onto the specified changeset')),
timeless@mozdev.org
rebase: change rebase help to talk about changesets and branch names
r9589 ('', 'collapse', False, _('collapse the rebased changesets')),
('', 'keep', False, _('keep original changesets')),
('', 'keepbranches', False, _('keep original branch names')),
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 ('', 'detach', False, _('force detaching of source from its original '
'branch')),
Stefano Tortarolo
Add rebase extension
r6906 ('c', 'continue', False, _('continue an interrupted rebase')),
Matt Mackall
fix up a bunch of check-code warnings
r10413 ('a', 'abort', False, _('abort an interrupted rebase'))] +
Stefano Tortarolo
Add rebase extension
r6906 templateopts,
Greg Ward
rebase: improve help text...
r10646 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
'hg rebase {-a|-c}'))
Stefano Tortarolo
Add rebase extension
r6906 }