##// END OF EJS Templates
merge with stable
merge with stable

File last commit:

r32175:456b4a32 default
r32245:3a755652 merge default
Show More
rebase.py
1521 lines | 59.6 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:
Matt Mackall
urls: bulk-change primary website URLs
r26421 https://mercurial-scm.org/wiki/RebaseExtension
Stefano Tortarolo
Add rebase extension
r6906 '''
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 from __future__ import absolute_import
import errno
import os
Yuya Nishihara
py3: move up symbol imports to enforce import-checker rules...
r29205
from mercurial.i18n import _
from mercurial.node import (
hex,
nullid,
nullrev,
short,
)
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 from mercurial import (
bookmarks,
cmdutil,
commands,
copies,
destutil,
Augie Fackler
rebase: refer to dirstateguard by its new name
r30490 dirstateguard,
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 error,
extensions,
hg,
lock,
timeless
rebase: rename merge to mergemod
r30271 merge as mergemod,
Augie Fackler
rebase: refer to checkunresolved by its new name
r30495 mergeutil,
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 obsolete,
patch,
phases,
registrar,
repair,
repoview,
revset,
scmutil,
Yuya Nishihara
revset: import set classes directly from smartset module...
r31023 smartset,
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 util,
)
release = lock.release
templateopts = commands.templateopts
Stefano Tortarolo
Add rebase extension
r6906
Christian Delahousse
rebase: added comments...
r26669 # The following constants are used throughout the rebase module. The ordering of
# their values must be maintained.
# Indicates that a revision needs to be rebased
Pierre-Yves David
rebase: add a 'revtodo' constant...
r23490 revtodo = -1
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 nullmerge = -2
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 revignored = -3
Laurent Charignon
rebase: remove an unused todo...
r27013 # successor in rebase destination
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 revprecursor = -4
Laurent Charignon
rebase: remove an unused todo...
r27013 # plain prune (no successor)
Laurent Charignon
rebase: don't rebase obsolete commits with no successor...
r27012 revpruned = -5
Laurent Charignon
rebase: refactoring to avoid repetition of expression...
r27014 revskipped = (revignored, revprecursor, revpruned)
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 cmdtable = {}
command = cmdutil.command(cmdtable)
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 testedwith = 'ships-with-hg-core'
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306
Ryan McElroy
rebase: factor out nothing to rebase return code...
r26671 def _nothingtorebase():
return 1
Siddharth Agarwal
rebase: backout changeset d755a9531fce...
r27976 def _savegraft(ctx, extra):
s = ctx.extra().get('source', None)
if s is not None:
extra['source'] = s
s = ctx.extra().get('intermediate-source', None)
if s is not None:
extra['intermediate-source'] = s
def _savebranch(ctx, extra):
extra['branch'] = ctx.branch()
Augie Fackler
rebase: rework extrafn handling to support multiple extrafns...
r19860 def _makeextrafn(copiers):
"""make an extrafn out of the given copy-functions.
A copy function takes a context and an extra dict, and mutates the
extra dict as needed based on the given context.
"""
def extrafn(ctx, extra):
for c in copiers:
c(ctx, extra)
return extrafn
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 def _destrebase(repo, sourceset, destspace=None):
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 """small wrapper around destmerge to pass the right extra args
Please wrap destutil.destmerge instead."""
return destutil.destmerge(repo, action='rebase', sourceset=sourceset,
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 onheadcheck=False, destspace=destspace)
Pierre-Yves David
rebase: extra default destination in its own function...
r26717
FUJIWARA Katsunori
revset: replace extpredicate by revsetpredicate of registrar...
r28394 revsetpredicate = registrar.revsetpredicate()
FUJIWARA Katsunori
revset: use delayregistrar to register predicate in extension easily...
r27586
@revsetpredicate('_destrebase')
Pierre-Yves David
rebase: rename and test '_destrebase'...
r26719 def _revsetdestrebase(repo, subset, x):
Pierre-Yves David
rebase: move destination computation in a revset...
r26301 # ``_rebasedefaultdest()``
# default destination for rebase.
# # XXX: Currently private because I expect the signature to change.
# # XXX: - bailing out in case of ambiguity vs returning all data.
# i18n: "_rebasedefaultdest" is a keyword
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 sourceset = None
if x is not None:
Yuya Nishihara
revset: import set classes directly from smartset module...
r31023 sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
return subset & smartset.baseset([_destrebase(repo, sourceset)])
Pierre-Yves David
rebase: move destination computation in a revset...
r26301
Kostia Balytskyi
rebase: introduce a rebaseruntime (RR) class...
r29358 class rebaseruntime(object):
"""This class is a container for rebase runtime state"""
Kostia Balytskyi
rebase: pass repo, ui and opts objects to the RR class constructor
r29399 def __init__(self, repo, ui, opts=None):
if opts is None:
opts = {}
self.repo = repo
self.ui = ui
self.opts = opts
Kostia Balytskyi
rebase: introduce a rebaseruntime (RR) class...
r29358 self.originalwd = None
self.external = nullrev
# Mapping between the old revision id and either what is the new rebased
# revision or what needs to be done with the old revision. The state
# dict will be what contains most of the rebase progress state.
self.state = {}
self.activebookmark = None
Kostia Balytskyi
rebase: move local variable 'currentbookmarks' to the RR class
r29475 self.currentbookmarks = None
Kostia Balytskyi
rebase: move local variable 'target' to the RR class
r29359 self.target = None
Kostia Balytskyi
rebase: move local variable 'skipped' to the RR class
r29360 self.skipped = set()
Kostia Balytskyi
rebase: move local variable 'targetancestors' to the RR class
r29361 self.targetancestors = set()
Kostia Balytskyi
rebase: introduce a rebaseruntime (RR) class...
r29358
Kostia Balytskyi
rebase: move collapse-related local variables to the RR class...
r29400 self.collapsef = opts.get('collapse', False)
self.collapsemsg = cmdutil.logmessage(ui, opts)
Kostia Balytskyi
rebase: move local variables 'date' and 'extrafns' to the RR class...
r29401 self.date = opts.get('date', None)
e = opts.get('extrafn') # internal, used by e.g. hgsubversion
self.extrafns = [_savegraft]
if e:
self.extrafns = [e]
Kostia Balytskyi
rebase: move collapse-related local variables to the RR class...
r29400
Kostia Balytskyi
rebase: move local variables related to keeping things unchanged to the RR...
r29402 self.keepf = opts.get('keep', False)
self.keepbranchesf = opts.get('keepbranches', False)
# keepopen is not meant for use on the command line, but by
# other extensions
self.keepopen = opts.get('keepopen', False)
Kostia Balytskyi
rebase: move local variable 'obsoletenotrebased' to the RR class
r29404 self.obsoletenotrebased = {}
Kostia Balytskyi
rebase: move local variables related to keeping things unchanged to the RR...
r29402
Durham Goode
rebase: add storestatus support for transactions...
r31224 def storestatus(self, tr=None):
Durham Goode
rebase: move storestatus onto rebaseruntime...
r31223 """Store the current status to allow recovery"""
Durham Goode
rebase: add storestatus support for transactions...
r31224 if tr:
tr.addfilegenerator('rebasestate', ('rebasestate',),
self._writestatus, location='plain')
else:
with self.repo.vfs("rebasestate", "w") as f:
self._writestatus(f)
def _writestatus(self, f):
Durham Goode
rebase: move state serialization to use unfiltered repo...
r31619 repo = self.repo.unfiltered()
Durham Goode
rebase: move storestatus onto rebaseruntime...
r31223 f.write(repo[self.originalwd].hex() + '\n')
f.write(repo[self.target].hex() + '\n')
f.write(repo[self.external].hex() + '\n')
f.write('%d\n' % int(self.collapsef))
f.write('%d\n' % int(self.keepf))
f.write('%d\n' % int(self.keepbranchesf))
f.write('%s\n' % (self.activebookmark or ''))
for d, v in self.state.iteritems():
oldrev = repo[d].hex()
if v >= 0:
newrev = repo[v].hex()
elif v == revtodo:
# To maintain format compatibility, we have to use nullid.
# Please do remove this special case when upgrading the format.
newrev = hex(nullid)
else:
newrev = v
f.write("%s:%s\n" % (oldrev, newrev))
repo.ui.debug('rebase status stored\n')
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 def restorestatus(self):
"""Restore a previously stored status"""
repo = self.repo
keepbranches = None
target = None
collapse = False
external = nullrev
activebookmark = None
state = {}
try:
f = repo.vfs("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))
elif i == 4:
keep = bool(int(l))
elif i == 5:
keepbranches = bool(int(l))
elif i == 6 and not (len(l) == 81 and ':' in l):
# line 6 is a recent addition, so for backwards
# compatibility check that the line doesn't look like the
# oldrev:newrev lines
activebookmark = l
else:
oldrev, newrev = l.split(':')
if newrev in (str(nullmerge), str(revignored),
str(revprecursor), str(revpruned)):
state[repo[oldrev].rev()] = int(newrev)
elif newrev == nullid:
state[repo[oldrev].rev()] = revtodo
# Legacy compat special case
else:
state[repo[oldrev].rev()] = repo[newrev].rev()
except IOError as err:
if err.errno != errno.ENOENT:
raise
cmdutil.wrongtooltocontinue(repo, _('rebase'))
if keepbranches is None:
raise error.Abort(_('.hg/rebasestate is incomplete'))
skipped = set()
# recompute the set of skipped revs
if not collapse:
seen = set([target])
for old, new in sorted(state.items()):
if new != revtodo and new in seen:
skipped.add(old)
seen.add(new)
repo.ui.debug('computed skipped revs: %s\n' %
(' '.join(str(r) for r in sorted(skipped)) or None))
repo.ui.debug('rebase status resumed\n')
Martin von Zweigbergk
rebase: unhide original working directory node as well (issue5219)...
r31297 _setrebasesetvisibility(repo, set(state.keys()) | set([originalwd]))
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403
self.originalwd = originalwd
self.target = target
self.state = state
self.skipped = skipped
self.collapsef = collapse
self.keepf = keep
self.keepbranchesf = keepbranches
self.external = external
self.activebookmark = activebookmark
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 def _handleskippingobsolete(self, rebaserevs, obsoleterevs, target):
"""Compute structures necessary for skipping obsolete revisions
rebaserevs: iterable of all revisions that are to be rebased
obsoleterevs: iterable of all obsolete revisions in rebaseset
target: a destination revision for the rebase operation
"""
self.obsoletenotrebased = {}
if not self.ui.configbool('experimental', 'rebaseskipobsolete',
default=True):
return
rebaseset = set(rebaserevs)
obsoleteset = set(obsoleterevs)
self.obsoletenotrebased = _computeobsoletenotrebased(self.repo,
obsoleteset, target)
skippedset = set(self.obsoletenotrebased)
_checkobsrebase(self.repo, self.ui, obsoleteset, rebaseset, skippedset)
Kostia Balytskyi
rebase: move abort/continue prep to be a method of the RR class...
r29472 def _prepareabortorcontinue(self, isabort):
try:
self.restorestatus()
Durham Goode
rebase: allow aborting if last-message.txt is missing...
r31225 self.collapsemsg = restorecollapsemsg(self.repo, isabort)
Kostia Balytskyi
rebase: move abort/continue prep to be a method of the RR class...
r29472 except error.RepoLookupError:
if isabort:
clearstatus(self.repo)
clearcollapsemsg(self.repo)
self.repo.ui.warn(_('rebase aborted (no revision is removed,'
' only broken state is cleared)\n'))
return 0
else:
msg = _('cannot continue inconsistent rebase')
hint = _('use "hg rebase --abort" to clear broken state')
raise error.Abort(msg, hint=hint)
if isabort:
return abort(self.repo, self.originalwd, self.target,
self.state, activebookmark=self.activebookmark)
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 obsrevs = (r for r, st in self.state.items() if st == revprecursor)
self._handleskippingobsolete(self.state.keys(), obsrevs, self.target)
Kostia Balytskyi
rebase: move abort/continue prep to be a method of the RR class...
r29472
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473 def _preparenewrebase(self, dest, rebaseset):
if dest is None:
return _nothingtorebase()
allowunstable = obsolete.isenabled(self.repo, obsolete.allowunstableopt)
if (not (self.keepf or allowunstable)
and self.repo.revs('first(children(%ld) - %ld)',
rebaseset, rebaseset)):
raise error.Abort(
_("can't remove original changesets with"
" unrebased descendants"),
hint=_('use --keep to keep original changesets'))
Simon Farnsworth
rebase: turn rebase revs into set before filtering obsolete...
r29610 obsrevs = _filterobsoleterevs(self.repo, set(rebaseset))
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 self._handleskippingobsolete(rebaseset, obsrevs, dest)
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473
result = buildstate(self.repo, dest, rebaseset, self.collapsef,
self.obsoletenotrebased)
if not result:
# Empty state built, nothing to rebase
self.ui.status(_('nothing to rebase\n'))
return _nothingtorebase()
Martin von Zweigbergk
rebase: abort if *any* commit in rebase set is public
r31302 for root in self.repo.set('roots(%ld)', rebaseset):
if not self.keepf and not root.mutable():
raise error.Abort(_("can't rebase public changeset %s")
% root,
hint=_("see 'hg help phases' for details"))
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473
(self.originalwd, self.target, self.state) = result
if self.collapsef:
self.targetancestors = self.repo.changelog.ancestors(
[self.target],
inclusive=True)
self.external = externalparent(self.repo, self.state,
self.targetancestors)
if dest.closesbranch() and not self.keepbranchesf:
self.ui.status(_('reopening closed branch head %s\n') % dest)
Durham Goode
rebase: move actual rebase into a single transaction...
r31226 def _performrebase(self, tr):
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 repo, ui, opts = self.repo, self.ui, self.opts
if self.keepbranchesf:
# insert _savebranch at the start of extrafns so if
# there's a user-provided extrafn it can clobber branch if
# desired
self.extrafns.insert(0, _savebranch)
if self.collapsef:
branches = set()
for rev in self.state:
branches.add(repo[rev].branch())
if len(branches) > 1:
raise error.Abort(_('cannot collapse multiple named '
'branches'))
# Rebase
if not self.targetancestors:
self.targetancestors = repo.changelog.ancestors([self.target],
inclusive=True)
# Keep track of the current bookmarks in order to reset them later
self.currentbookmarks = repo._bookmarks.copy()
self.activebookmark = self.activebookmark or repo._activebookmark
if self.activebookmark:
bookmarks.deactivate(repo)
Durham Goode
rebase: allow aborting if last-message.txt is missing...
r31225 # Store the state before we begin so users can run 'hg rebase --abort'
# if we fail before the transaction closes.
self.storestatus()
Xidorn Quan
rebase: rebase changesets in topo order (issue5370) (BC)...
r30007 sortedrevs = repo.revs('sort(%ld, -topo)', self.state)
timeless
rebase: properly calculate total commits to rebase (issue5347)...
r29872 cands = [k for k, v in self.state.iteritems() if v == revtodo]
total = len(cands)
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 pos = 0
Kostia Balytskyi
rebase: remove sortedstate-related confusion...
r29552 for rev in sortedrevs:
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 ctx = repo[rev]
desc = '%d:%s "%s"' % (ctx.rev(), ctx,
ctx.description().split('\n', 1)[0])
names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
if names:
desc += ' (%s)' % ' '.join(names)
if self.state[rev] == revtodo:
timeless
rebase: properly calculate total commits to rebase (issue5347)...
r29872 pos += 1
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 ui.status(_('rebasing %s\n') % desc)
ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)),
_('changesets'), total)
p1, p2, base = defineparents(repo, rev, self.target,
self.state,
self.targetancestors,
self.obsoletenotrebased)
Durham Goode
rebase: move actual rebase into a single transaction...
r31226 self.storestatus(tr=tr)
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 storecollapsemsg(repo, self.collapsemsg)
if len(repo[None].parents()) == 2:
repo.ui.debug('resuming interrupted rebase\n')
else:
try:
ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
'rebase')
stats = rebasenode(repo, rev, p1, base, self.state,
self.collapsef, self.target)
if stats and stats[3] > 0:
raise error.InterventionRequired(
_('unresolved conflicts (see hg '
'resolve, then hg rebase --continue)'))
finally:
ui.setconfig('ui', 'forcemerge', '', 'rebase')
if not self.collapsef:
merging = p2 != nullrev
editform = cmdutil.mergeeditform(merging, 'rebase')
editor = cmdutil.getcommiteditor(editform=editform, **opts)
newnode = concludenode(repo, rev, p1, p2,
Kostia Balytskyi
rebase: replace extrafn field with _makeextrafn invocations...
r29551 extrafn=_makeextrafn(self.extrafns),
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 editor=editor,
keepbranches=self.keepbranchesf,
date=self.date)
else:
# Skip commit if we are collapsing
repo.dirstate.beginparentchange()
repo.setparents(repo[p1].node())
repo.dirstate.endparentchange()
newnode = None
# Update the state
if newnode is not None:
self.state[rev] = repo[newnode].rev()
ui.debug('rebased as %s\n' % short(newnode))
else:
if not self.collapsef:
ui.warn(_('note: rebase of %d:%s created no changes '
'to commit\n') % (rev, ctx))
self.skipped.add(rev)
self.state[rev] = p1
ui.debug('next revision set to %s\n' % p1)
elif self.state[rev] == nullmerge:
ui.debug('ignoring null merge rebase of %s\n' % rev)
elif self.state[rev] == revignored:
ui.status(_('not rebasing ignored %s\n') % desc)
elif self.state[rev] == revprecursor:
targetctx = repo[self.obsoletenotrebased[rev]]
desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx,
targetctx.description().split('\n', 1)[0])
msg = _('note: not rebasing %s, already in destination as %s\n')
ui.status(msg % (desc, desctarget))
elif self.state[rev] == revpruned:
msg = _('note: not rebasing %s, it has no successor\n')
ui.status(msg % desc)
else:
ui.status(_('already rebased %s as %s\n') %
(desc, repo[self.state[rev]]))
ui.progress(_('rebasing'), None)
ui.note(_('rebase merging completed\n'))
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 def _finishrebase(self):
repo, ui, opts = self.repo, self.ui, self.opts
if self.collapsef and not self.keepopen:
p1, p2, _base = defineparents(repo, min(self.state),
self.target, self.state,
self.targetancestors,
self.obsoletenotrebased)
editopt = opts.get('edit')
editform = 'rebase.collapse'
if self.collapsemsg:
commitmsg = self.collapsemsg
else:
commitmsg = 'Collapsed revision'
for rebased in self.state:
if rebased not in self.skipped and\
self.state[rebased] > nullmerge:
commitmsg += '\n* %s' % repo[rebased].description()
editopt = True
editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
Kostia Balytskyi
rebase: remove sortedstate-related confusion...
r29552 revtoreuse = max(self.state)
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
try:
newnode = concludenode(repo, revtoreuse, p1, self.external,
commitmsg=commitmsg,
extrafn=_makeextrafn(self.extrafns),
editor=editor,
keepbranches=self.keepbranchesf,
date=self.date)
dsguard.close()
release(dsguard)
except error.InterventionRequired:
dsguard.close()
release(dsguard)
raise
except Exception:
release(dsguard)
raise
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 if newnode is None:
newrev = self.target
else:
newrev = repo[newnode].rev()
for oldrev in self.state.iterkeys():
if self.state[oldrev] > nullmerge:
self.state[oldrev] = newrev
if 'qtip' in repo.tags():
updatemq(repo, self.state, self.skipped, **opts)
if self.currentbookmarks:
# Nodeids are needed to reset bookmarks
nstate = {}
for k, v in self.state.iteritems():
if v > nullmerge:
nstate[repo[k].node()] = repo[v].node()
elif v == revprecursor:
succ = self.obsoletenotrebased[k]
nstate[repo[k].node()] = repo[succ].node()
# XXX this is the same as dest.node() for the non-continue path --
# this should probably be cleaned up
targetnode = repo[self.target].node()
# restore original working directory
# (we do this before stripping)
newwd = self.state.get(self.originalwd, self.originalwd)
if newwd == revprecursor:
newwd = self.obsoletenotrebased[self.originalwd]
elif newwd < 0:
# original directory is a parent of rebase set root or ignored
newwd = self.originalwd
if newwd not in [c.rev() for c in repo[None].parents()]:
ui.note(_("update back to initial working directory parent\n"))
hg.updaterepo(repo, newwd, False)
Durham Goode
rebase: move bookmark update to before rebase clearing...
r30459 if self.currentbookmarks:
with repo.transaction('bookmark') as tr:
updatebookmarks(repo, targetnode, nstate,
self.currentbookmarks, tr)
if self.activebookmark not in repo._bookmarks:
# active bookmark was divergent one and has been deleted
self.activebookmark = None
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 if not self.keepf:
collapsedas = None
if self.collapsef:
collapsedas = newnode
clearrebased(ui, repo, self.state, self.skipped, collapsedas)
clearstatus(repo)
clearcollapsemsg(repo)
ui.note(_("rebase completed\n"))
util.unlinkpath(repo.sjoin('undo'), ignoremissing=True)
if self.skipped:
skippedlen = len(self.skipped)
ui.note(_("%d revisions have been skipped\n") % skippedlen)
if (self.activebookmark and
repo['.'].node() == repo._bookmarks[self.activebookmark]):
bookmarks.activate(repo, self.activebookmark)
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 @command('rebase',
[('s', 'source', '',
Matt Mackall
rebase: attempt to clarify --base
r22789 _('rebase the specified changeset and descendants'), _('REV')),
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 ('b', 'base', '',
Matt Mackall
rebase: attempt to clarify --base
r22789 _('rebase everything from branching point of specified changeset'),
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 _('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')),
Nat Mote
rebase: add short -k option for --keep...
r25025 ('k', 'keep', False, _('keep original changesets')),
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 ('', '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)')),
David Soria Parra
rebase: add a deprecated -i/--interactive flag...
r22382 ('i', 'interactive', 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,
Patrick Mezard
rebase: remove second broken synopsis line (issue3172)...
r17325 _('[-s REV | -b REV] [-d REV] [OPTION]'))
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.
timeless
rebase: simplify documentation about public commits...
r27454 Published commits cannot be rebased (see :hg:`help phases`).
To copy commits, see :hg:`help graft`.
Kevin Bullock
rebase: mention phases in the help...
r18516
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 If you don't specify a destination changeset (``-d/--dest``), rebase
will use the same logic as :hg:`merge` to pick a destination. if
the current branch contains exactly one other head, the other head
is merged with by default. Otherwise, an explicit revision with
which to merge with must be provided. (destination changeset is not
modified by rebasing, but new changesets are added as its
descendants.)
Greg Ward
rebase: improve help text...
r10646
FUJIWARA Katsunori
doc: prevent non-literal text block from being treated as literal one...
r27956 Here are the ways to select changesets:
timeless
rebase: simplify documentation about selecting commits to rebase
r27455
1. Explicitly select them using ``--rev``.
Greg Ward
rebase: improve help text...
r10646
timeless
rebase: simplify documentation about selecting commits to rebase
r27455 2. Use ``--source`` to select a root changeset and include all of its
FUJIWARA Katsunori
doc: use correct indentation for enumeration...
r27959 descendants.
timeless
rebase: simplify documentation about selecting commits to rebase
r27455
3. Use ``--base`` to select a changeset; rebase will find ancestors
FUJIWARA Katsunori
doc: use correct indentation for enumeration...
r27959 and their descendants which are not also ancestors of the destination.
Pierre-Yves David
rebase: mention --rev in the help...
r18518
timeless
rebase: restore help for rebase w/o args (issue5059)...
r27932 4. If you do not specify any of ``--rev``, ``source``, or ``--base``,
FUJIWARA Katsunori
doc: use correct indentation for enumeration...
r27959 rebase will use ``--base .`` as above.
timeless
rebase: restore help for rebase w/o args (issue5059)...
r27932
timeless
rebase: simplify documentation about --keep...
r27456 Rebase will destroy original changesets unless you use ``--keep``.
It will also move your bookmarks (even if you do).
Some changesets may be dropped if they do not contribute changes
(e.g. merges from the destination branch).
Greg Ward
rebase: improve help text...
r10646
timeless
rebase: simplify documentation about heads
r27457 Unlike ``merge``, rebase will do nothing if you are at the branch tip of
a named branch with two heads. You will need to explicitly specify source
and/or destination.
Stefano Tortarolo
Add rebase extension
r6906
timeless
rebase: mention help merge-tools in help
r28001 If you need to use a tool to automate merge/conflict decisions, you
can specify one with ``--tool``, see :hg:`help merge-tools`.
timeless
rebase: document that tool does not apply to deleted files
r28002 As a caveat: the tool will not be used to mediate when a file was
deleted, there is no hook presently available for this.
timeless
rebase: mention help merge-tools in help
r28001
timeless
rebase: mention conflict in documentation instead of merge
r27458 If a rebase is interrupted to manually resolve a conflict, 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
Matt Mackall
rebase: add help examples
r22790 .. container:: verbose
Examples:
- move "local changes" (current commit back to branching point)
to the current branch tip after a pull::
hg rebase
- move a single changeset to the stable branch::
hg rebase -r 5f493448 -d stable
- splice a commit and all its descendants onto another part of history::
hg rebase --source c0c3 --dest 4cf9
- rebase everything on a branch marked by a bookmark onto the
default branch::
hg rebase --base myfeature --dest default
- collapse a sequence of changes into a single commit::
hg rebase --collapse -r 1520:1525 -d .
- move a named branch while preserving its name::
hg rebase -r "branch(featureX)" -d 1.3 --keepbranches
Ryan McElroy
rebase: add flag to require destination...
r31558 Configuration Options:
You can make rebase require a destination if you set the following config
FUJIWARA Katsunori
help: apply bulk fixes for indentation and literal blocking issues...
r32085 option::
Ryan McElroy
rebase: add flag to require destination...
r31558
[commands]
FUJIWARA Katsunori
rebase: fix incorrect configuration example...
r32084 rebase.requiredest = True
Ryan McElroy
rebase: add flag to require destination...
r31558
Return Values:
FUJIWARA Katsunori
rebase: add description about exit code when there are unresolved conflicts
r19971 Returns 0 on success, 1 if nothing to rebase or there are
unresolved conflicts.
Matt Mackall
rebase: add help examples
r22790
Stefano Tortarolo
Add rebase extension
r6906 """
Kostia Balytskyi
rebase: pass repo, ui and opts objects to the RR class constructor
r29399 rbsrt = rebaseruntime(repo, ui, opts)
Matt Mackall
rebase: add --edit switch
r15219
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', [])
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 # search default destination in this space
# used in the 'hg pull --rebase' case, see issue 5214.
destspace = opts.get('_destspace')
Stefano Tortarolo
Add rebase extension
r6906 contf = opts.get('continue')
abortf = opts.get('abort')
David Soria Parra
rebase: add a deprecated -i/--interactive flag...
r22382 if opts.get('interactive'):
timeless@mozdev.org
rebase: enable histedit for useful help with it
r26496 try:
if extensions.find('histedit'):
enablehistedit = ''
except KeyError:
enablehistedit = " --config extensions.histedit="
help = "hg%s help -e histedit" % enablehistedit
David Soria Parra
rebase: add a deprecated -i/--interactive flag...
r22382 msg = _("interactive history editing is supported by the "
timeless@mozdev.org
rebase: factor out histedit help command
r26494 "'histedit' extension (see \"%s\")") % help
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(msg)
David Soria Parra
rebase: add a deprecated -i/--interactive flag...
r22382
Kostia Balytskyi
rebase: move collapse-related local variables to the RR class...
r29400 if rbsrt.collapsemsg and not rbsrt.collapsef:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(
Radomir Dopieralski
rebase: add -m/--message to rebase --collapse (issue2389)...
r13661 _('message can only be specified with collapse'))
Stefano Tortarolo
Add rebase extension
r6906 if contf or abortf:
if contf and abortf:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('cannot use both abort and continue'))
Kostia Balytskyi
rebase: move collapse-related local variables to the RR class...
r29400 if rbsrt.collapsef:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(
Matt Mackall
rebase: use usual util.abort rather than error.ParseError
r11285 _('cannot use collapse with continue or abort'))
Martin Geisler
remove unnecessary outer parenthesis in if-statements
r8117 if srcf or basef or destf:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(
Stefano Tortarolo
Add rebase extension
r6906 _('abort and continue do not allow specifying revisions'))
liscju
rebase: fix warning about ignoring tool option on rebase continue (issue4698)...
r26165 if abortf and opts.get('tool', False):
Stefano Tortarolo
rebase: add --tool argument for specifying merge tool
r13856 ui.warn(_('tool option will be ignored\n'))
timeless
rebase: check for conflicts before continuing...
r30273 if contf:
ms = mergemod.mergestate.read(repo)
Augie Fackler
rebase: refer to checkunresolved by its new name
r30495 mergeutil.checkunresolved(ms)
Stefano Tortarolo
Add rebase extension
r6906
Kostia Balytskyi
rebase: move abort/continue prep to be a method of the RR class...
r29472 retcode = rbsrt._prepareabortorcontinue(abortf)
if retcode is not None:
return retcode
Stefano Tortarolo
Add rebase extension
r6906 else:
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 dest, rebaseset = _definesets(ui, repo, destf, srcf, basef, revf,
destspace=destspace)
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473 retcode = rbsrt._preparenewrebase(dest, rebaseset)
if retcode is not None:
return retcode
Mads Kiilerich
rebase: tell when reopening a closed branch head...
r21027
Durham Goode
rebase: move actual rebase into a single transaction...
r31226 with repo.transaction('rebase') as tr:
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
Durham Goode
rebase: move actual rebase into a single transaction...
r31226 try:
rbsrt._performrebase(tr)
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 dsguard.close()
release(dsguard)
Durham Goode
rebase: move actual rebase into a single transaction...
r31226 except error.InterventionRequired:
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 dsguard.close()
release(dsguard)
Durham Goode
rebase: move actual rebase into a single transaction...
r31226 tr.close()
raise
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 except Exception:
release(dsguard)
raise
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 rbsrt._finishrebase()
Stefano Tortarolo
Add rebase extension
r6906 finally:
Ronny Pfannschmidt
switch lock releasing in the extensions from gc to explicit
r8112 release(lock, wlock)
Stefano Tortarolo
Add rebase extension
r6906
Gregory Szorc
rebase: don't use mutable default argument value
r31395 def _definesets(ui, repo, destf=None, srcf=None, basef=None, revf=None,
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 destspace=None):
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136 """use revisions argument to define destination and rebase set
"""
Pierre-Yves David
rebase: explicitly tests for None...
r31431 if revf is None:
revf = []
Gregory Szorc
rebase: don't use mutable default argument value
r31395
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 # destspace is here to work around issues with `hg pull --rebase` see
# issue5214 for details
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136 if srcf and basef:
raise error.Abort(_('cannot specify both a source and a base'))
if revf and basef:
raise error.Abort(_('cannot specify both a revision and a base'))
if revf and srcf:
raise error.Abort(_('cannot specify both a revision and a source'))
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
Ryan McElroy
rebase: allow destination-free continue and abort (issue5513)
r31731 if ui.configbool('commands', 'rebase.requiredest') and not destf:
raise error.Abort(_('you must specify a destination'),
hint=_('use: hg rebase -d REV'))
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136 if destf:
dest = scmutil.revsingle(repo, destf)
if revf:
rebaseset = scmutil.revrange(repo, revf)
if not rebaseset:
ui.status(_('empty "rev" revision set - nothing to rebase\n'))
return None, None
elif srcf:
src = scmutil.revrange(repo, [srcf])
if not src:
ui.status(_('empty "source" revision set - nothing to rebase\n'))
return None, None
rebaseset = repo.revs('(%ld)::', src)
assert rebaseset
else:
base = scmutil.revrange(repo, [basef or '.'])
if not base:
ui.status(_('empty "base" revision set - '
"can't compute rebase set\n"))
return None, None
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 if not destf:
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 dest = repo[_destrebase(repo, base, destspace=destspace)]
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 destf = str(dest)
Jun Wu
rebase: calculate ancestors for --base separately (issue5420)...
r30580 roots = [] # selected children of branching points
bpbase = {} # {branchingpoint: [origbase]}
for b in base: # group bases by branching points
bp = repo.revs('ancestor(%d, %d)', b, dest).first()
bpbase[bp] = bpbase.get(bp, []) + [b]
if None in bpbase:
# emulate the old behavior, showing "nothing to rebase" (a better
# behavior may be abort with "cannot find branching point" error)
bpbase.clear()
for bp, bs in bpbase.iteritems(): # calculate roots
roots += list(repo.revs('children(%d) & ancestors(%ld)', bp, bs))
rebaseset = repo.revs('%ld::', roots)
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136
if not rebaseset:
# transform to list because smartsets are not comparable to
# lists. This should be improved to honor laziness of
# smartset.
if list(base) == [dest.rev()]:
if basef:
ui.status(_('nothing to rebase - %s is both "base"'
' and destination\n') % dest)
else:
ui.status(_('nothing to rebase - working directory '
'parent is also destination\n'))
elif not repo.revs('%ld - ::%d', base, dest):
if basef:
ui.status(_('nothing to rebase - "base" %s is '
'already an ancestor of destination '
'%s\n') %
('+'.join(str(repo[r]) for r in base),
dest))
else:
ui.status(_('nothing to rebase - working '
'directory parent is already an '
'ancestor of destination %s\n') % dest)
else: # can it happen?
ui.status(_('nothing to rebase from %s to %s\n') %
('+'.join(str(repo[r]) for r in base), dest))
return None, None
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189
if not destf:
Pierre-Yves David
destutil: add the ability to specify a search space for rebase destination...
r29043 dest = repo[_destrebase(repo, rebaseset, destspace=destspace)]
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 destf = str(dest)
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136 return dest, rebaseset
Mads Kiilerich
rebase: refactor and rename checkexternal - it is a getter more than a setter
r19955 def externalparent(repo, state, targetancestors):
"""Return the revision that should be used as the second parent
when the revisions in state is collapsed on top of targetancestors.
Abort if there is more than one parent.
Stefano Tortarolo
rebase: refactoring...
r10351 """
Mads Kiilerich
rebase: refactor and rename checkexternal - it is a getter more than a setter
r19955 parents = set()
Stefano Tortarolo
rebase: refactoring...
r10351 source = min(state)
for rev in state:
if rev == source:
continue
for p in repo[rev].parents():
if (p.rev() not in state
and p.rev() not in targetancestors):
Mads Kiilerich
rebase: refactor and rename checkexternal - it is a getter more than a setter
r19955 parents.add(p.rev())
if not parents:
return nullrev
if len(parents) == 1:
return parents.pop()
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('unable to collapse on top of %s, there is more '
Mads Kiilerich
rebase: improve error message for more than one external parent
r19956 'than one external parent: %s') %
(max(targetancestors),
', '.join(str(p) for p in sorted(parents))))
Stefano Tortarolo
rebase: refactoring...
r10351
timeless@mozdev.org
rebase: avoid losing branch commits with --keepbranch (issue4835)
r26360 def concludenode(repo, rev, p1, p2, commitmsg=None, editor=None, extrafn=None,
Stanislau Hlebik
rebase: add date parameter to concludenode function...
r26868 keepbranches=False, date=None):
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 '''Commit the wd changes with parents p1 and p2. Reuse commit info from rev
but also store useful information in extra.
Mads Kiilerich
rebase: clarify naming of variables holding node hashes - don't call them rev
r23459 Return node of committed revision.'''
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 repo.setparents(repo[p1].node(), repo[p2].node())
ctx = repo[rev]
if commitmsg is None:
commitmsg = ctx.description()
keepbranch = keepbranches and repo[p1].branch() != ctx.branch()
extra = {'rebase_source': ctx.hex()}
if extrafn:
extrafn(ctx, extra)
Pierre-Yves David
rebase: do not retract phase boundary by hand...
r22038
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 targetphase = max(ctx.phase(), phases.draft)
overrides = {('phases', 'new-commit'): targetphase}
with repo.ui.configoverride(overrides, 'rebase'):
if keepbranch:
repo.ui.setconfig('ui', 'allowemptycommit', True)
# Commit might fail if unresolved files exist
if date is None:
date = ctx.date()
newnode = repo.commit(text=commitmsg, user=ctx.user(),
date=date, extra=extra, editor=editor)
Pierre-Yves David
rebase: do not retract phase boundary by hand...
r22038
Durham Goode
rebase: use one dirstateguard for entire rebase...
r31514 repo.dirstate.setbranch(repo[newnode].branch())
return newnode
Stefano Tortarolo
Add rebase extension
r6906
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 def rebasenode(repo, rev, p1, base, state, collapse, target):
'Rebase a single revision rev on top of p1 using base as merge ancestor'
Stefano Tortarolo
Add rebase extension
r6906 # Merge phase
Stefano Tortarolo
rebase: refactoring...
r10351 # Update to target and merge it with local
Mads Kiilerich
rebase: avoid redundant repo[rev].rev() - just keep working in rev space
r23461 if repo['.'].rev() != p1:
repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
timeless
rebase: rename merge to mergemod
r30271 mergemod.update(repo, p1, False, True)
Stefano Tortarolo
Add rebase extension
r6906 else:
Stefano Tortarolo
rebase: refactoring...
r10351 repo.ui.debug(" already in target\n")
FUJIWARA Katsunori
dirstate: make dirstate.write() callers pass transaction object to it...
r26748 repo.dirstate.write(repo.currenttransaction())
Mads Kiilerich
rebase: avoid redundant repo[rev].rev() - just keep working in rev space
r23461 repo.ui.debug(" merge against %d:%s\n" % (rev, repo[rev]))
Pierre-Yves David
rebase: fix selection of base used when rebasing merge (issue4041)...
r19969 if base is not None:
Mads Kiilerich
rebase: avoid redundant repo[rev].rev() - just keep working in rev space
r23461 repo.ui.debug(" detach base %d:%s\n" % (base, repo[base]))
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.
timeless
rebase: rename merge to mergemod
r30271 stats = mergemod.update(repo, rev, True, True, base, collapse,
labels=['dest', 'source'])
Matt Mackall
rebase: move duplicatecopies next to merge...
r22905 if collapse:
copies.duplicatecopies(repo, rev, target)
else:
# If we're not using --collapse, we need to
# duplicate copies between the revision we're
# rebasing and its first parent, but *not*
# duplicate any copies that have already been
# performed in the destination.
p1rev = repo[rev].p1().rev()
copies.duplicatecopies(repo, rev, p1rev, skiprev=target)
return stats
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 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:
Pierre-Yves David
rebase: use `last` instead of direct indexing...
r22820 return state[candidates.first()]
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 else:
return None
Augie Fackler
rebase: un-wrap function signature since it fits in 80 columns
r30674 def _checkobsrebase(repo, ui, rebaseobsrevs, rebasesetrevs, rebaseobsskipped):
Laurent Charignon
rebase: refactor of error handling code path for rebaseskipobsolete...
r28685 """
Abort if rebase will create divergence or rebase is noop because of markers
`rebaseobsrevs`: set of obsolete revision in source
`rebasesetrevs`: set of revisions to be rebased from source
`rebaseobsskipped`: set of revisions from source skipped because they have
successors in destination
"""
# Obsolete node with successors not in dest leads to divergence
divergenceok = ui.configbool('experimental',
'allowdivergence')
divergencebasecandidates = rebaseobsrevs - rebaseobsskipped
if divergencebasecandidates and not divergenceok:
divhashes = (str(repo[r])
for r in divergencebasecandidates)
msg = _("this rebase will cause "
"divergences from: %s")
h = _("to force the rebase please set "
"experimental.allowdivergence=True")
raise error.Abort(msg % (",".join(divhashes),), hint=h)
timeless
rebase: handle successor targets (issue5198)...
r29063 def defineparents(repo, rev, target, state, targetancestors,
obsoletenotrebased):
Stefano Tortarolo
Add rebase extension
r6906 'Return the new parent relationship of the revision that will be rebased'
parents = repo[rev].parents()
p1 = p2 = nullrev
timeless
rebase: handle successor targets (issue5198)...
r29063 rp1 = None
Stefano Tortarolo
Add rebase extension
r6906
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p1n = parents[0].rev()
if p1n in targetancestors:
Stefano Tortarolo
Add rebase extension
r6906 p1 = target
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 elif p1n in state:
if state[p1n] == nullmerge:
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 p1 = target
Laurent Charignon
rebase: refactoring to avoid repetition of expression...
r27014 elif state[p1n] in revskipped:
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p1 = nearestrebased(repo, p1n, state)
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 if p1 is None:
p1 = target
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352 else:
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p1 = state[p1n]
else: # p1n external
Stefano Tortarolo
Add rebase extension
r6906 p1 = target
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p2 = p1n
Stefano Tortarolo
Add rebase extension
r6906
if len(parents) == 2 and parents[1].rev() not in targetancestors:
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p2n = parents[1].rev()
Stefano Tortarolo
Add rebase extension
r6906 # interesting second parent
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 if p2n in state:
if p1 == target: # p1n in targetancestors or external
p1 = state[p2n]
timeless
rebase: handle successor targets (issue5198)...
r29063 if p1 == revprecursor:
rp1 = obsoletenotrebased[p2n]
Laurent Charignon
rebase: refactoring to avoid repetition of expression...
r27014 elif state[p2n] in revskipped:
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p2 = nearestrebased(repo, p2n, state)
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 if p2 is None:
# no ancestors rebased yet, detach
p2 = target
Stefano Tortarolo
Add rebase extension
r6906 else:
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p2 = state[p2n]
else: # p2n external
if p2 != nullrev: # p1n external too => rev is a merged revision
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('cannot use revision %d as base, result '
Stefano Tortarolo
Add rebase extension
r6906 'would have 3 parents') % rev)
Matt Mackall
rebase: fix some weird mixed-case naming
r22906 p2 = p2n
Stefano Tortarolo
rebase: refactoring...
r10351 repo.ui.debug(" future parents are %d and %d\n" %
timeless
rebase: handle successor targets (issue5198)...
r29063 (repo[rp1 or p1].rev(), repo[p2].rev()))
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484
Martijn Pieters
rebase: better way to detect non-detaching revisions (issue5044)...
r27963 if not any(p.rev() in state for p in parents):
# Case (1) root changeset of a non-detaching rebase set.
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 # Let the merge mechanism find the base itself.
base = None
elif not repo[rev].p2():
# Case (2) detaching the node with a single parent, use this parent
base = repo[rev].p1().rev()
else:
Mads Kiilerich
rebase: clarify comment about merge ancestor when rebasing merges...
r23732 # Assuming there is a p1, this is the case where there also is a p2.
# We are thus rebasing a merge and need to pick the right merge base.
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 #
# Imagine we have:
Mads Kiilerich
rebase: clarify comment about merge ancestor when rebasing merges...
r23732 # - M: current rebase revision in this step
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 # - A: one parent of M
Mads Kiilerich
rebase: clarify comment about merge ancestor when rebasing merges...
r23732 # - B: other parent of M
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 # - D: destination of this merge step (p1 var)
#
Mads Kiilerich
rebase: clarify comment about merge ancestor when rebasing merges...
r23732 # Consider the case where D is a descendant of A or B and the other is
# 'outside'. In this case, the right merge base is the D ancestor.
#
# An informal proof, assuming A is 'outside' and B is the D ancestor:
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 #
# If we pick B as the base, the merge involves:
# - changes from B to M (actual changeset payload)
# - changes from B to D (induced by rebase) as D is a rebased
# version of B)
# Which exactly represent the rebase operation.
#
Mads Kiilerich
rebase: clarify comment about merge ancestor when rebasing merges...
r23732 # If we pick A as the base, the merge involves:
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 # - changes from A to M (actual changeset payload)
# - changes from A to D (with include changes between unrelated A and B
# plus changes induced by rebase)
# Which does not represent anything sensible and creates a lot of
Mads Kiilerich
rebase: clarify comment about merge ancestor when rebasing merges...
r23732 # conflicts. A is thus not the right choice - B is.
#
# Note: The base found in this 'proof' is only correct in the specified
# case. This base does not make sense if is not D a descendant of A or B
# or if the other is not parent 'outside' (especially not if the other
# parent has been rebased). The current implementation does not
# make it feasible to consider different cases separately. In these
# other cases we currently just leave it to the user to correctly
# resolve an impossible merge using a wrong ancestor.
timeless
rebase: handle successor targets (issue5198)...
r29063 #
# xx, p1 could be -4, and both parents could probably be -4...
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 for p in repo[rev].parents():
if state.get(p.rev()) == p1:
base = p.rev()
break
else: # fallback when base not found
base = None
# Raise because this function is called wrong (see issue 4106)
raise AssertionError('no base found to rebase on '
'(defineparents called wrong)')
timeless
rebase: handle successor targets (issue5198)...
r29063 return rp1 or p1, p2, base
Stefano Tortarolo
Add rebase extension
r6906
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]
Mads Kiilerich
rebase: show a note for updated mq patches...
r23520 repo.ui.note(_('updating mq patch %s to %s:%s\n') %
(name, state[rev], repo[state[rev]]))
Nicolas Dumazet
rebase: small cosmetic cleanups
r11537 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
Laurent Charignon
rebase: use bookmarks.recordchange instead of bookmarks.write...
r27059 def updatebookmarks(repo, targetnode, nstate, originalbookmarks, tr):
Siddharth Agarwal
rebase: delete divergent bookmarks on destination (issue3685)...
r18514 'Move bookmarks to their correct changesets, and delete divergent ones'
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 marks = repo._bookmarks
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884 for k, v in originalbookmarks.iteritems():
if v in nstate:
Siddharth Agarwal
rebase: remove bogus nullmerge check in updatebookmarks...
r18512 # update the bookmarks for revs that have moved
marks[k] = nstate[v]
Siddharth Agarwal
rebase: derive node from target rev (issue3802)...
r18549 bookmarks.deletedivergent(repo, [targetnode], k)
Laurent Charignon
rebase: use bookmarks.recordchange instead of bookmarks.write...
r27059 marks.recordchange(tr)
Stefano Tortarolo
rebase: reset bookmarks (issue2265 and issue2873)
r14884
liscju
rebase: adds storing collapse message (issue4792)...
r28185 def storecollapsemsg(repo, collapsemsg):
'Store the collapse message to allow recovery'
collapsemsg = collapsemsg or ''
f = repo.vfs("last-message.txt", "w")
f.write("%s\n" % collapsemsg)
f.close()
def clearcollapsemsg(repo):
'Remove collapse message file'
Mads Kiilerich
vfs: use repo.vfs.unlinkpath
r31311 repo.vfs.unlinkpath("last-message.txt", ignoremissing=True)
liscju
rebase: adds storing collapse message (issue4792)...
r28185
Durham Goode
rebase: allow aborting if last-message.txt is missing...
r31225 def restorecollapsemsg(repo, isabort):
liscju
rebase: adds storing collapse message (issue4792)...
r28185 'Restore previously stored collapse message'
try:
f = repo.vfs("last-message.txt")
collapsemsg = f.readline().strip()
f.close()
except IOError as err:
if err.errno != errno.ENOENT:
raise
Durham Goode
rebase: allow aborting if last-message.txt is missing...
r31225 if isabort:
# Oh well, just abort like normal
collapsemsg = ''
else:
raise error.Abort(_('missing .hg/last-message.txt for rebase'))
liscju
rebase: adds storing collapse message (issue4792)...
r28185 return collapsemsg
Stefano Tortarolo
Add rebase extension
r6906 def clearstatus(repo):
'Remove the status files'
Pierre-Yves David
rebase: ensure rebase revision remains visible (issue4504)...
r23970 _clearrebasesetvisibiliy(repo)
Mads Kiilerich
vfs: use repo.vfs.unlinkpath
r31311 repo.vfs.unlinkpath("rebasestate", ignoremissing=True)
Stefano Tortarolo
Add rebase extension
r6906
Jordi Gutiérrez Hermoso
rebase: clear merge when aborting before any rebasing (issue4661)...
r25070 def needupdate(repo, state):
'''check whether we should `update --clean` away from a merge, or if
somehow the working dir got forcibly updated, e.g. by older hg'''
Augie Fackler
commands: inline definition of localrepo.parents() and drop the method (API)...
r27167 parents = [p.rev() for p in repo[None].parents()]
Jordi Gutiérrez Hermoso
rebase: clear merge when aborting before any rebasing (issue4661)...
r25070
# Are we in a merge state at all?
if len(parents) < 2:
return False
# We should be standing on the first as-of-yet unrebased commit.
firstunrebased = min([old for old, new in state.iteritems()
if new == nullrev])
if firstunrebased in parents:
Matt Mackall
rebase: don't clobber wd on --abort when we've updated away (issue4009)
r19516 return True
return False
Tony Tung
rebase: restore bookmark state on abort...
r24758 def abort(repo, originalwd, target, state, activebookmark=None):
'''Restore the repository to its original state. Additional args:
activebookmark: the name of the bookmark that should be active after the
restore'''
Christian Delahousse
rebase: properly abort when destination is public (issue4896)...
r26677
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 try:
# If the first commits in the rebased set get skipped during the rebase,
# their values within the state mapping will be the target rev id. The
# dstates list must must not contain the target rev (issue4896)
dstates = [s for s in state.values() if s >= 0 and s != target]
immutable = [d for d in dstates if not repo[d].mutable()]
cleanup = True
if immutable:
repo.ui.warn(_("warning: can't clean up public changesets %s\n")
% ', '.join(str(repo[r]) for r in immutable),
timeless
rebase: use single quotes in use warning
r29966 hint=_("see 'hg help phases' for details"))
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 cleanup = False
Matt Mackall
rebase: properly calculate descendant set when aborting (issue3332)...
r16280
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 descendants = set()
if dstates:
descendants = set(repo.changelog.descendants(dstates))
if descendants - set(dstates):
repo.ui.warn(_("warning: new changesets detected on target branch, "
"can't strip\n"))
cleanup = False
Matt Mackall
rebase: allow aborting when descendants detected...
r19518
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 if cleanup:
timeless
rebase: update working directory when aborting (issue5084)
r27988 shouldupdate = False
rebased = filter(lambda x: x >= 0 and x != target, state.values())
if rebased:
strippoints = [
c.node() for c in repo.set('roots(%ld)', rebased)]
Durham Goode
rebase: clear updatestate during rebase --abort in more cases...
r31222
updateifonnodes = set(rebased)
updateifonnodes.add(target)
updateifonnodes.add(originalwd)
shouldupdate = repo['.'].rev() in updateifonnodes
timeless
rebase: update working directory when aborting (issue5084)
r27988
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 # Update away from the rebase if necessary
timeless
rebase: update working directory when aborting (issue5084)
r27988 if shouldupdate or needupdate(repo, state):
timeless
rebase: rename merge to mergemod
r30271 mergemod.update(repo, originalwd, False, True)
Matt Mackall
rebase: don't clobber wd on --abort when we've updated away (issue4009)
r19516
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 # Strip from the first rebased revision
if rebased:
# no backup of rebased cset versions needed
repair.strip(repo.ui, repo, strippoints)
Matt Mackall
rebase: allow aborting when descendants detected...
r19518
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 if activebookmark and activebookmark in repo._bookmarks:
bookmarks.activate(repo, activebookmark)
Tony Tung
rebase: restore bookmark state on abort...
r24758
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 finally:
clearstatus(repo)
liscju
rebase: adds storing collapse message (issue4792)...
r28185 clearcollapsemsg(repo)
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 repo.ui.warn(_('rebase aborted\n'))
Matt Mackall
rebase: allow aborting when descendants detected...
r19518 return 0
Stefano Tortarolo
Add rebase extension
r6906
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 def buildstate(repo, dest, rebaseset, collapse, obsoletenotrebased):
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 '''
Martin von Zweigbergk
rebase: unhide original working directory node as well (issue5219)...
r31297 originalwd = repo['.'].rev()
_setrebasesetvisibility(repo, set(rebaseset) | set([originalwd]))
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]):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('cannot rebase onto an applied mq patch'))
Greg Ward
rebase: always check if rebasing onto an applied mq patch....
r10672
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
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('no matching revisions'))
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 roots.sort()
Martin von Zweigbergk
rebase: don't update state dict same way for each root...
r32175 state = dict.fromkeys(rebaseset, revtodo)
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 detachset = set()
for root in roots:
commonbase = root.ancestor(dest)
if commonbase == root:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('source is ancestor of destination'))
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 if commonbase == dest:
Mads Kiilerich
rebase: allow rebasing children of wd to wd if a new branch has been set (BC)...
r31380 wctx = repo[None]
if dest == wctx.p1():
# when rebasing to '.', it will use the current wd branch name
samebranch = root.branch() == wctx.branch()
else:
samebranch = root.branch() == dest.branch()
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 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
Martin von Zweigbergk
rebase: make debug logging more consistent...
r29936 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 # 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 | new parent is <dest> | parents in ::<dest> are |
# | ::<dest> | | remapped to <dest> |
# +------------------+----------------------+-------------------------+
# | unrelated source | new parent is <dest> | ambiguous, abort |
# +------------------+----------------------+-------------------------+
#
# The actual abort is handled by `defineparents`
if len(root.parents()) <= 1:
# ancestors of <root> not ancestors of <dest>
detachset.update(repo.changelog.findmissingrevs([commonbase.rev()],
[root.rev()]))
for r in detachset:
if r not in state:
state[r] = nullmerge
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 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
Mads Kiilerich
spelling: fix some minor issues found by spell checker
r18644 # the revision should be ignored but that `defineparents` should search
# a rebase destination that make sense regarding rebased topology.
Pierre-Yves David
rebase: properly handle unrebased revision between rebased one...
r18447 rebasedomain = set(repo.revs('%ld::%ld', rebaseset, rebaseset))
for ignored in set(rebasedomain) - set(rebaseset):
state[ignored] = revignored
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 for r in obsoletenotrebased:
Laurent Charignon
rebase: don't rebase obsolete commits with no successor...
r27012 if obsoletenotrebased[r] is None:
state[r] = revpruned
else:
state[r] = revprecursor
Martin von Zweigbergk
rebase: unhide original working directory node as well (issue5219)...
r31297 return originalwd, dest.rev(), state
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: do not invent successor to skipped changeset...
r18444 def clearrebased(ui, repo, state, skipped, collapsedas=None):
Pierre-Yves David
rebase: properly handle --collapse when creating obsolescence marker...
r17613 """dispose of rebased revision at the end of the rebase
If `collapsedas` is not None, the rebase was a collapse whose result if the
`collapsedas` node."""
Durham Goode
obsolete: add createmarkers option...
r22951 if obsolete.isenabled(repo, obsolete.createmarkersopt):
Pierre-Yves David
rebase: allow creation obsolescence relation instead of stripping...
r17612 markers = []
for rev, newrev in sorted(state.items()):
if newrev >= 0:
Pierre-Yves David
rebase: do not invent successor to skipped changeset...
r18444 if rev in skipped:
succs = ()
elif collapsedas is not None:
succs = (repo[collapsedas],)
else:
succs = (repo[newrev],)
markers.append((repo[rev], succs))
Pierre-Yves David
rebase: allow creation obsolescence relation instead of stripping...
r17612 if markers:
obsolete.createmarkers(repo, markers)
else:
Pierre-Yves David
rebase: lose the comparison to `nullmerge`...
r18446 rebased = [rev for rev in state if state[rev] > nullmerge]
Pierre-Yves David
rebase: allow creation obsolescence relation instead of stripping...
r17612 if rebased:
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 stripped = []
for root in repo.set('roots(%ld)', rebased):
if set(repo.changelog.descendants([root.rev()])) - set(state):
ui.warn(_("warning: new changesets detected "
"on source branch, not stripping\n"))
else:
stripped.append(root.node())
if stripped:
Pierre-Yves David
rebase: allow creation obsolescence relation instead of stripping...
r17612 # backup the old csets by default
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 repair.strip(ui, repo, stripped, "all")
Pierre-Yves David
rebase: extract final changesets cleanup logic in a dedicated function...
r17611
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'
liscju
rebase: add returning value from pullrebase function...
r26960 ret = None
Stefano Tortarolo
Add rebase extension
r6906 if opts.get('rebase'):
Ryan McElroy
rebase: abort hg pull --rebase if rebase.requiredest is set (issue5514)...
r31733 if ui.configbool('commands', 'rebase.requiredest'):
msg = _('rebase destination required by configuration')
hint = _('use hg pull followed by hg rebase -d DEST')
raise error.Abort(msg, hint=hint)
Pierre-Yves David
rebase: lock the repo during the full rebase operation...
r26029 wlock = lock = None
try:
wlock = repo.wlock()
lock = repo.lock()
if opts.get('update'):
del opts['update']
ui.debug('--update and --rebase are not compatible, ignoring '
'the update flag\n')
Stefano Tortarolo
Add rebase extension
r6906
Valters Vingolds
rebase: fail-fast the pull if working dir is not clean (BC)...
r30725 cmdutil.checkunfinished(repo)
Valters Vingolds
rebase: provide detailed hint to abort message if working dir is not clean...
r30755 cmdutil.bailifchanged(repo, hint=_('cannot pull with rebase: '
'please commit or shelve your changes first'))
Valters Vingolds
rebase: fail-fast the pull if working dir is not clean (BC)...
r30725
Pierre-Yves David
rebase: lock the repo during the full rebase operation...
r26029 revsprepull = len(repo)
origpostincoming = commands.postincoming
def _dummy(*args, **kwargs):
pass
commands.postincoming = _dummy
try:
liscju
rebase: add returning value from pullrebase function...
r26960 ret = orig(ui, repo, *args, **opts)
Pierre-Yves David
rebase: lock the repo during the full rebase operation...
r26029 finally:
commands.postincoming = origpostincoming
revspostpull = len(repo)
if revspostpull > revsprepull:
# --rev option from pull conflict with rebase own --rev
# dropping it
if 'rev' in opts:
del opts['rev']
# positional argument from pull conflicts with rebase's own
# --source.
if 'source' in opts:
del opts['source']
Pierre-Yves David
rebase: restrict rebase destination to the pulled set (issue5214)...
r29044 # revsprepull is the len of the repo, not revnum of tip.
destspace = list(repo.changelog.revs(start=revsprepull))
opts['_destspace'] = destspace
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 try:
rebase(ui, repo, **opts)
except error.NoMergeDestAbort:
# we can maybe update instead
Pierre-Yves David
rebase: perform update through the 'update' command...
r28118 rev, _a, _b = destutil.destupdate(repo)
Pierre-Yves David
rebase: choose default destination the same way as 'hg merge' (BC)...
r28189 if rev == repo['.'].rev():
ui.status(_('nothing to rebase\n'))
else:
ui.status(_('nothing to rebase - updating instead\n'))
Pierre-Yves David
rebase: perform update through the 'update' command...
r28118 # not passing argument to get the bare update behavior
# with warning and trumpets
commands.update(ui, repo)
Sune Foldager
rebase: improve output of hg pull --rebase (issue2072)
r10628 finally:
Pierre-Yves David
rebase: lock the repo during the full rebase operation...
r26029 release(lock, wlock)
Stefano Tortarolo
Add rebase extension
r6906 else:
Adrian Buehlmann
rebase: add option --tool/-t for 'pull --rebase'...
r14444 if opts.get('tool'):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('--tool can only be used with --rebase'))
liscju
rebase: add returning value from pullrebase function...
r26960 ret = orig(ui, repo, *args, **opts)
return ret
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: ensure rebase revision remains visible (issue4504)...
r23970 def _setrebasesetvisibility(repo, revs):
"""store the currently rebased set on the repo object
This is used by another function to prevent rebased revision to because
Martin von Zweigbergk
rebase: fix code comment to refer to right issue (4504, not 4505)...
r30865 hidden (see issue4504)"""
Pierre-Yves David
rebase: ensure rebase revision remains visible (issue4504)...
r23970 repo = repo.unfiltered()
repo._rebaseset = revs
# invalidate cache if visibility changes
hiddens = repo.filteredrevcache.get('visible', set())
if revs & hiddens:
repo.invalidatevolatilesets()
def _clearrebasesetvisibiliy(repo):
"""remove rebaseset data from the repo"""
repo = repo.unfiltered()
if '_rebaseset' in vars(repo):
del repo._rebaseset
def _rebasedvisible(orig, repo):
Martin von Zweigbergk
rebase: fix code comment to refer to right issue (4504, not 4505)...
r30865 """ensure rebased revs stay visible (see issue4504)"""
Pierre-Yves David
rebase: ensure rebase revision remains visible (issue4504)...
r23970 blockers = orig(repo)
blockers.update(getattr(repo, '_rebaseset', ()))
return blockers
Laurent Charignon
rebase: small refactoring to allow better extensibility from extensions...
r27790 def _filterobsoleterevs(repo, revs):
"""returns a set of the obsolete revisions in revs"""
return set(r for r in revs if repo[r].obsolete())
Laurent Charignon
rebase: minor refactoring of _computeobsoletenotrebased...
r27744 def _computeobsoletenotrebased(repo, rebaseobsrevs, dest):
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 """return a mapping obsolete => successor for all obsolete nodes to be
Laurent Charignon
rebase: don't rebase obsolete commits with no successor...
r27012 rebased that have a successors in the destination
Mads Kiilerich
spelling: fixes of non-dictionary words
r30332 obsolete => None entries in the mapping indicate nodes with no successor"""
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 obsoletenotrebased = {}
Mads Kiilerich
spelling: trivial spell checking
r26781 # Build a mapping successor => obsolete nodes for the obsolete
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 # nodes to be rebased
allsuccessors = {}
Pierre-Yves David
rebase: use a direct reference to repo.changelog...
r26674 cl = repo.changelog
Laurent Charignon
rebase: minor refactoring of _computeobsoletenotrebased...
r27744 for r in rebaseobsrevs:
node = cl.node(r)
for s in obsolete.allsuccessors(repo.obsstore, [node]):
try:
allsuccessors[cl.rev(s)] = cl.rev(node)
except LookupError:
pass
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349
if allsuccessors:
# Look for successors of obsolete nodes to be rebased among
# the ancestors of dest
Pierre-Yves David
rebase: use a direct reference to repo.changelog...
r26674 ancs = cl.ancestors([repo[dest].rev()],
stoprev=min(allsuccessors),
inclusive=True)
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 for s in allsuccessors:
if s in ancs:
obsoletenotrebased[allsuccessors[s]] = s
Laurent Charignon
rebase: don't rebase obsolete commits with no successor...
r27012 elif (s == allsuccessors[s] and
allsuccessors.values().count(s) == 1):
# plain prune
obsoletenotrebased[s] = None
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 return obsoletenotrebased
Bryan O'Sullivan
summary: indicate if a rebase is underway
r19214 def summaryhook(ui, repo):
Valters Vingolds
rebase: use repo.vfs.exists in 'hg summary' hook
r30709 if not repo.vfs.exists('rebasestate'):
Bryan O'Sullivan
summary: indicate if a rebase is underway
r19214 return
FUJIWARA Katsunori
rebase: catch RepoLookupError at restoring rebase state for summary...
r19849 try:
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 rbsrt = rebaseruntime(repo, ui, {})
rbsrt.restorestatus()
state = rbsrt.state
FUJIWARA Katsunori
rebase: catch RepoLookupError at restoring rebase state for summary...
r19849 except error.RepoLookupError:
# i18n: column positioning for "hg summary"
msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
ui.write(msg)
return
Pierre-Yves David
rebase: use '>= 0' to know is a revision was rebased...
r23489 numrebased = len([i for i in state.itervalues() if i >= 0])
Bryan O'Sullivan
summary: indicate if a rebase is underway
r19214 # i18n: column positioning for "hg summary"
ui.write(_('rebase: %s, %s (rebase --continue)\n') %
(ui.label(_('%d rebased'), 'rebase.rebased') % numrebased,
ui.label(_('%d remaining'), 'rebase.remaining') %
(len(state) - numrebased)))
Stefano Tortarolo
Add rebase extension
r6906 def uisetup(ui):
Pierre-Yves David
rebase: ensure rebase revision remains visible (issue4504)...
r23970 #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")))
Bryan O'Sullivan
summary: indicate if a rebase is underway
r19214 cmdutil.summaryhooks.add('rebase', summaryhook)
Matt Mackall
rebase: add checkunfinished support (issue3955)
r19478 cmdutil.unfinishedstates.append(
Matt Mackall
checkunfinished: accommodate histedit quirk...
r19496 ['rebasestate', False, False, _('rebase in progress'),
Matt Mackall
rebase: add checkunfinished support (issue3955)
r19478 _("use 'hg rebase --continue' or 'hg rebase --abort'")])
timeless
rebase: hook afterresolvedstates
r27626 cmdutil.afterresolvedstates.append(
['rebasestate', _('hg rebase --continue')])
Pierre-Yves David
rebase: ensure rebase revision remains visible (issue4504)...
r23970 # ensure rebased rev are not hidden
extensions.wrapfunction(repoview, '_getdynamicblockers', _rebasedvisible)