##// END OF EJS Templates
py3: use default dict iterator instead of iterkeys...
py3: use default dict iterator instead of iterkeys These are the easy cases. Some cases in manifest.py will require more careful inspection. Differential Revision: https://phab.mercurial-scm.org/D2315

File last commit:

r36313:23864678 default
r36313:23864678 default
Show More
rebase.py
1870 lines | 74.0 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 (
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,
obsutil: move 'allsuccessors' to the new modules...
r33146 obsutil,
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 patch,
phases,
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 pycompat,
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 registrar,
repair,
revset,
Jun Wu
rebase: initial support for multiple destinations...
r34007 revsetlang,
Pulkit Goyal
py3: make hgext/rebase.py use absolute_import
r29128 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
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
Jun Wu
rebase: change internal format to support destination map...
r34006 revtodostr = '-1'
Jun Wu
rebase: remove revprecursor and revpruned states (BC)...
r33842
# legacy revstates no longer needed in current code
Jun Wu
rebase: remove revignored and nullmerge states...
r33844 # -2: nullmerge, -3: revignored, -4: revprecursor, -5: revpruned
legacystates = {'-2', '-3', '-4', '-5'}
Stefano Tortarolo
rebase: add --detach option to detach intermediate revisions (issue1950)...
r10352
Adrian Buehlmann
rebase: use cmdutil.command decorator
r14306 cmdtable = {}
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 command = registrar.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
Jun Wu
rebase: extract ctx description logic to a function...
r33840 def _ctxdesc(ctx):
"""short description for a context"""
desc = '%d:%s "%s"' % (ctx.rev(), ctx,
ctx.description().split('\n', 1)[0])
repo = ctx.repo()
Martin von Zweigbergk
rebase: also include other namespaces in changeset description...
r34291 names = []
for nsname, ns in repo.names.iteritems():
if nsname == 'branches':
continue
names.extend(ns.names(repo, ctx.node()))
Jun Wu
rebase: extract ctx description logic to a function...
r33840 if names:
desc += ' (%s)' % ' '.join(names)
return desc
Kostia Balytskyi
rebase: introduce a rebaseruntime (RR) class...
r29358 class rebaseruntime(object):
"""This class is a container for rebase runtime state"""
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 def __init__(self, repo, ui, inmemory=False, opts=None):
Kostia Balytskyi
rebase: pass repo, ui and opts objects to the RR class constructor
r29399 if opts is None:
opts = {}
Jun Wu
rebase: remove complex unhiding code...
r34096 # prepared: whether we have rebasestate prepared or not. Currently it
# decides whether "self.repo" is unfiltered or not.
# The rebasestate has explicit hash to hash instructions not depending
# on visibility. If rebasestate exists (in-memory or on-disk), use
# unfiltered repo to avoid visibility issues.
# Before knowing rebasestate (i.e. when starting a new rebase (not
# --continue or --abort)), the original repo should be used so
# visibility-dependent revsets are correct.
self.prepared = False
self._repo = repo
Kostia Balytskyi
rebase: pass repo, ui and opts objects to the RR class constructor
r29399 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
Jun Wu
rebase: change internal format to support destination map...
r34006 self.destmap = {}
Kostia Balytskyi
rebase: move local variable 'skipped' to the RR class
r29360 self.skipped = 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 = {}
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 self.obsoletewithoutsuccessorindestination = set()
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 self.inmemory = inmemory
Kostia Balytskyi
rebase: move local variables related to keeping things unchanged to the RR...
r29402
Jun Wu
rebase: remove complex unhiding code...
r34096 @property
def repo(self):
if self.prepared:
return self._repo.unfiltered()
else:
return self._repo
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):
Jun Wu
rebase: remove unnecessary '.unfiltered()' calls...
r34097 repo = self.repo
assert repo.filtername is None
Durham Goode
rebase: move storestatus onto rebaseruntime...
r31223 f.write(repo[self.originalwd].hex() + '\n')
Jun Wu
rebase: change internal format to support destination map...
r34006 # was "dest". we now write dest per src root below.
f.write('\n')
Durham Goode
rebase: move storestatus onto rebaseruntime...
r31223 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 ''))
Jun Wu
rebase: change internal format to support destination map...
r34006 destmap = self.destmap
Durham Goode
rebase: move storestatus onto rebaseruntime...
r31223 for d, v in self.state.iteritems():
oldrev = repo[d].hex()
if v >= 0:
newrev = repo[v].hex()
else:
Pulkit Goyal
py3: use "%d" to convert integer to bytes...
r35933 newrev = "%d" % v
Jun Wu
rebase: change internal format to support destination map...
r34006 destnode = repo[destmap[d]].hex()
f.write("%s:%s:%s\n" % (oldrev, newrev, destnode))
Durham Goode
rebase: move storestatus onto rebaseruntime...
r31223 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"""
Jun Wu
rebase: remove complex unhiding code...
r34096 self.prepared = True
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 repo = self.repo
Jun Wu
rebase: remove unnecessary '.unfiltered()' calls...
r34097 assert repo.filtername is None
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 keepbranches = None
Jun Wu
rebase: change internal format to support destination map...
r34006 legacydest = None
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 collapse = False
external = nullrev
activebookmark = None
state = {}
Jun Wu
rebase: change internal format to support destination map...
r34006 destmap = {}
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403
try:
f = repo.vfs("rebasestate")
for i, l in enumerate(f.read().splitlines()):
if i == 0:
originalwd = repo[l].rev()
elif i == 1:
Jun Wu
rebase: change internal format to support destination map...
r34006 # this line should be empty in newer version. but legacy
# clients may still use it
if l:
legacydest = repo[l].rev()
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 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:
Jun Wu
rebase: change internal format to support destination map...
r34006 args = l.split(':')
oldrev = args[0]
newrev = args[1]
Jun Wu
rebase: remove revprecursor and revpruned states (BC)...
r33842 if newrev in legacystates:
continue
Jun Wu
rebase: change internal format to support destination map...
r34006 if len(args) > 2:
destnode = args[2]
else:
destnode = legacydest
destmap[repo[oldrev].rev()] = repo[destnode].rev()
if newrev in (nullid, revtodostr):
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 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:
Jun Wu
rebase: change internal format to support destination map...
r34006 seen = set(destmap.values())
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 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' %
Gregory Szorc
py3: port string formatting...
r36164 (' '.join('%d' % r for r in sorted(skipped)) or ''))
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 repo.ui.debug('rebase status resumed\n')
self.originalwd = originalwd
Jun Wu
rebase: change internal format to support destination map...
r34006 self.destmap = destmap
Kostia Balytskyi
rebase: move restorestestatus function to be a method of the RR class
r29403 self.state = state
self.skipped = skipped
self.collapsef = collapse
self.keepf = keep
self.keepbranchesf = keepbranches
self.external = external
self.activebookmark = activebookmark
Jun Wu
rebase: change internal format to support destination map...
r34006 def _handleskippingobsolete(self, obsoleterevs, destmap):
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 """Compute structures necessary for skipping obsolete revisions
obsoleterevs: iterable of all obsolete revisions in rebaseset
Jun Wu
rebase: change internal format to support destination map...
r34006 destmap: {srcrev: destrev} destination revisions
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 """
self.obsoletenotrebased = {}
Boris Feld
configitems: register the 'experimental.rebaseskipobsolete' config
r34493 if not self.ui.configbool('experimental', 'rebaseskipobsolete'):
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 return
obsoleteset = set(obsoleterevs)
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 (self.obsoletenotrebased,
self.obsoletewithoutsuccessorindestination,
obsoleteextinctsuccessors) = _computeobsoletenotrebased(
self.repo, obsoleteset, destmap)
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479 skippedset = set(self.obsoletenotrebased)
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 skippedset.update(self.obsoletewithoutsuccessorindestination)
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 skippedset.update(obsoleteextinctsuccessors)
Jun Wu
rebase: remove rebaseset from _checkobsrebase...
r33845 _checkobsrebase(self.repo, self.ui, obsoleteset, skippedset)
Kostia Balytskyi
rebase: move handling of obsolete commits to be a separate RR class method
r29479
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:
Jun Wu
rebase: change internal format to support destination map...
r34006 return abort(self.repo, self.originalwd, self.destmap,
Kostia Balytskyi
rebase: move abort/continue prep to be a method of the RR class...
r29472 self.state, activebookmark=self.activebookmark)
Jun Wu
rebase: change internal format to support destination map...
r34006 def _preparenewrebase(self, destmap):
if not destmap:
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473 return _nothingtorebase()
Jun Wu
rebase: change internal format to support destination map...
r34006 rebaseset = destmap.keys()
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473 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'))
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 result = buildstate(self.repo, destmap, self.collapsef)
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473
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
Jun Wu
rebase: change internal format to support destination map...
r34006 (self.originalwd, self.destmap, self.state) = result
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473 if self.collapsef:
Jun Wu
rebase: change internal format to support destination map...
r34006 dests = set(self.destmap.values())
if len(dests) != 1:
raise error.Abort(
_('--collapse does not work with multiple destinations'))
destrev = next(iter(dests))
destancestors = self.repo.changelog.ancestors([destrev],
Jun Wu
rebase: remove self.destancestors...
r33846 inclusive=True)
self.external = externalparent(self.repo, self.state, destancestors)
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473
Jun Wu
rebase: change internal format to support destination map...
r34006 for destrev in sorted(set(destmap.values())):
dest = self.repo[destrev]
if dest.closesbranch() and not self.keepbranchesf:
self.ui.status(_('reopening closed branch head %s\n') % dest)
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473
Jun Wu
rebase: remove complex unhiding code...
r34096 self.prepared = True
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473
Phil Cohen
rebase: extract _assignworkingcopy...
r35334 def _assignworkingcopy(self):
Phil Cohen
rebase: add the --inmemory option flag; assign a wctx object for the rebase...
r35291 if self.inmemory:
from mercurial.context import overlayworkingctx
self.wctx = overlayworkingctx(self.repo)
Phil Cohen
tests: add a simple test for in-memory rebase...
r35385 self.repo.ui.debug("rebasing in-memory\n")
Phil Cohen
rebase: add the --inmemory option flag; assign a wctx object for the rebase...
r35291 else:
self.wctx = self.repo[None]
Phil Cohen
tests: add a simple test for in-memory rebase...
r35385 self.repo.ui.debug("rebasing on disk\n")
Phil Cohen
rebase: switch ui.log calls to common style...
r35503 self.repo.ui.log("rebase", "", rebase_imm_used=self.wctx.isinmemory())
Phil Cohen
rebase: extract _assignworkingcopy...
r35334
def _performrebase(self, tr):
self._assignworkingcopy()
repo, ui = self.repo, self.ui
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 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'))
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 # Calculate self.obsoletenotrebased
obsrevs = _filterobsoleterevs(self.repo, self.state)
self._handleskippingobsolete(obsrevs, self.destmap)
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477
Jun Wu
rebase: use scmutil.cleanupnodes (issue5606) (BC)...
r33332 # Keep track of the active bookmarks in order to reset them later
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 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()
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
Jun Wu
rebase: sort destmap topologically...
r34008 for subset in sortsource(self.destmap):
pos = self._performrebasesubset(tr, subset, pos, total)
ui.progress(_('rebasing'), None)
ui.note(_('rebase merging completed\n'))
def _performrebasesubset(self, tr, subset, pos, total):
repo, ui, opts = self.repo, self.ui, self.opts
sortedrevs = repo.revs('sort(%ld, -topo)', subset)
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 allowdivergence = self.ui.configbool(
'experimental', 'evolution.allowdivergence')
if not allowdivergence:
sortedrevs -= repo.revs(
'descendants(%ld) and not %ld',
self.obsoletewithoutsuccessorindestination,
self.obsoletewithoutsuccessorindestination,
)
Kostia Balytskyi
rebase: remove sortedstate-related confusion...
r29552 for rev in sortedrevs:
Jun Wu
rebase: change internal format to support destination map...
r34006 dest = self.destmap[rev]
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 ctx = repo[rev]
Jun Wu
rebase: extract ctx description logic to a function...
r33840 desc = _ctxdesc(ctx)
Martin von Zweigbergk
rebase: allow rebase even if some revisions need no rebase (BC) (issue5422)...
r32272 if self.state[rev] == rev:
ui.status(_('already rebased %s\n') % desc)
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 elif (not allowdivergence
and rev in self.obsoletewithoutsuccessorindestination):
msg = _('note: not rebasing %s and its descendants as '
'this would cause divergence\n') % desc
repo.ui.status(msg)
self.skipped.add(rev)
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 elif rev in self.obsoletenotrebased:
succ = self.obsoletenotrebased[rev]
if succ is None:
msg = _('note: not rebasing %s, it has no '
'successor\n') % desc
else:
Jun Wu
rebase: use _ctxdesc in one more place...
r34011 succdesc = _ctxdesc(repo[succ])
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 msg = (_('note: not rebasing %s, already in '
'destination as %s\n') % (desc, succdesc))
repo.ui.status(msg)
# Make clearrebased aware state[rev] is not a true successor
self.skipped.add(rev)
# Record rev as moved to its desired destination in self.state.
# This helps bookmark and working parent movement.
dest = max(adjustdest(repo, rev, self.destmap, self.state,
self.skipped))
self.state[rev] = dest
Martin von Zweigbergk
rebase: allow rebase even if some revisions need no rebase (BC) (issue5422)...
r32272 elif 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)
Jun Wu
rebase: change internal format to support destination map...
r34006 p1, p2, base = defineparents(repo, rev, self.destmap,
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 self.state, self.skipped,
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 self.obsoletenotrebased)
Durham Goode
rebase: add config to move rebase into a single transaction...
r33569 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,
Phil Cohen
rebase: pass wctx to rebasenode()...
r35317 self.collapsef, dest, wctx=self.wctx)
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 if stats and stats[3] > 0:
Phil Cohen
rebaseruntime: raise InMemoryMergeConflictsError on merge conflicts...
r35293 if self.wctx.isinmemory():
raise error.InMemoryMergeConflictsError()
else:
raise error.InterventionRequired(
_('unresolved conflicts (see hg '
'resolve, then hg rebase --continue)'))
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 finally:
ui.setconfig('ui', 'forcemerge', '', 'rebase')
if not self.collapsef:
merging = p2 != nullrev
editform = cmdutil.mergeeditform(merging, 'rebase')
Pulkit Goyal
py3: use pycompat.strkwargs() to convert kwargs' key to str...
r35935 editor = cmdutil.getcommiteditor(editform=editform,
**pycompat.strkwargs(opts))
Phil Cohen
rebase: add concludememorynode(), and call it when rebasing in-memory...
r35320 if self.wctx.isinmemory():
newnode = concludememorynode(repo, rev, p1, p2,
wctx=self.wctx,
extrafn=_makeextrafn(self.extrafns),
editor=editor,
keepbranches=self.keepbranchesf,
date=self.date)
mergemod.mergestate.clean(repo)
else:
newnode = concludenode(repo, rev, p1, p2,
extrafn=_makeextrafn(self.extrafns),
editor=editor,
keepbranches=self.keepbranchesf,
date=self.date)
Jeremy Fitzhardinge
rebase: make sure merge state is cleaned up for no-op rebases (issue5494)...
r32313 if newnode is None:
# If it ended up being a no-op commit, then the normal
# merge state clean-up path doesn't happen, so do it
# here. Fix issue5494
mergemod.mergestate.clean(repo)
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 else:
# Skip commit if we are collapsing
Phil Cohen
rebase: do not update if IMM; instead, set the overlaywctx's parents...
r35318 if self.wctx.isinmemory():
self.wctx.setbase(repo[p1])
else:
repo.setparents(repo[p1].node())
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 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
Pulkit Goyal
py3: use "%d" to convert integers to bytes...
r36203 ui.debug('next revision set to %d\n' % p1)
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477 else:
ui.status(_('already rebased %s as %s\n') %
(desc, repo[self.state[rev]]))
Jun Wu
rebase: sort destmap topologically...
r34008 return pos
Kostia Balytskyi
rebase: move core rebase logic to be a method of the RR class
r29477
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
Pulkit Goyal
rebase: add support to output nodechanges...
r34884 fm = ui.formatter('rebase', opts)
fm.startitem()
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 if self.collapsef and not self.keepopen:
Jun Wu
rebase: change internal format to support destination map...
r34006 p1, p2, _base = defineparents(repo, min(self.state), self.destmap,
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 self.state, self.skipped,
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 self.obsoletenotrebased)
editopt = opts.get('edit')
editform = 'rebase.collapse'
if self.collapsemsg:
commitmsg = self.collapsemsg
else:
commitmsg = 'Collapsed revision'
Yuya Nishihara
rebase: sort collapsed revisions in commit message (issue5643)...
r33624 for rebased in sorted(self.state):
Jun Wu
rebase: remove "state >= revtodo" condition...
r33847 if rebased not in self.skipped:
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 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 when using rebase.singletransaction...
r33621
dsguard = None
Phil Cohen
rebase: add concludememorynode(), and call it when rebasing in-memory...
r35320 if self.inmemory:
newnode = concludememorynode(repo, revtoreuse, p1,
self.external,
commitmsg=commitmsg,
extrafn=_makeextrafn(self.extrafns),
editor=editor,
keepbranches=self.keepbranchesf,
date=self.date, wctx=self.wctx)
else:
Phil Cohen
rebase: don't take out a dirstate guard for in-memory rebase...
r35496 if ui.configbool('rebase', 'singletransaction'):
dsguard = dirstateguard.dirstateguard(repo, 'rebase')
Phil Cohen
rebase: add concludememorynode(), and call it when rebasing in-memory...
r35320 with util.acceptintervention(dsguard):
newnode = concludenode(repo, revtoreuse, p1, self.external,
commitmsg=commitmsg,
extrafn=_makeextrafn(self.extrafns),
editor=editor,
keepbranches=self.keepbranchesf,
date=self.date)
Jun Wu
rebase: only change self.state when collapsing in _finishrebase...
r33864 if newnode is not None:
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 newrev = repo[newnode].rev()
Augie Fackler
py3: use default dict iterator instead of iterkeys...
r36313 for oldrev in self.state:
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 self.state[oldrev] = newrev
if 'qtip' in repo.tags():
updatemq(repo, self.state, self.skipped, **opts)
# restore original working directory
# (we do this before stripping)
newwd = self.state.get(self.originalwd, self.originalwd)
Jun Wu
rebase: remove revprecursor and revpruned states (BC)...
r33842 if newwd < 0:
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 # original directory is a parent of rebase set root or ignored
newwd = self.originalwd
Phil Cohen
rebase: do not update if IMM; instead, set the overlaywctx's parents...
r35318 if (newwd not in [c.rev() for c in repo[None].parents()] and
not self.inmemory):
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 ui.note(_("update back to initial working directory parent\n"))
hg.updaterepo(repo, newwd, False)
Jun Wu
rebase: move bookmarks with --keep (issue5682)...
r34364 collapsedas = None
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 if not self.keepf:
if self.collapsef:
collapsedas = newnode
Martin von Zweigbergk
merge with stable
r34366 clearrebased(ui, repo, self.destmap, self.state, self.skipped,
Pulkit Goyal
rebase: add support to output nodechanges...
r34884 collapsedas, self.keepf, fm=fm)
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478
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)
Pulkit Goyal
rebase: add support to output nodechanges...
r34884 fm.end()
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478
Jun Wu
rebase: use scmutil.cleanupnodes (issue5606) (BC)...
r33332 if (self.activebookmark and self.activebookmark in repo._bookmarks and
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 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'))] +
Yuya Nishihara
rebase: drop --style option...
r34964 cmdutil.formatteropts,
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
Jun Wu
rebase: enable multidest by default...
r35288 If ``--source`` or ``--rev`` is used, special names ``SRC`` and ``ALLSRC``
can be used in ``--dest``. Destination would be calculated per source
revision with ``SRC`` substituted by that single source revision and
``ALLSRC`` substituted by all source revisions.
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
Jun Wu
rebase: enable multidest by default...
r35288 - stabilize orphaned changesets so history looks linear::
hg rebase -r 'orphan()-obsolete()'\
-d 'first(max((successors(max(roots(ALLSRC) & ::SRC)^)-obsolete())::) +\
max(::((roots(ALLSRC) & ::SRC)^)-obsolete()))'
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
Durham Goode
rebase: add config to move rebase into a single transaction...
r33569 By default, rebase will close the transaction after each commit. For
performance purposes, you can configure rebase to use a single transaction
across the entire rebase. WARNING: This setting introduces a significant
risk of losing the work you've done in a rebase if the rebase aborts
unexpectedly::
[rebase]
singletransaction = True
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 By default, rebase writes to the working copy, but you can configure it to
run in-memory for for better performance, and to allow it to run if the
working copy is dirty::
[rebase]
experimental.inmemory = 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 """
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 inmemory = ui.configbool('rebase', 'experimental.inmemory')
Phil Cohen
rebase: don't run IMM if running rebase in a transaction...
r35719 if (opts.get('continue') or opts.get('abort') or
repo.currenttransaction() is not None):
Phil Cohen
rebase: rerun a rebase on-disk if IMM merge conflicts arise...
r35321 # in-memory rebase is not compatible with resuming rebases.
Phil Cohen
rebase: don't run IMM if running rebase in a transaction...
r35719 # (Or if it is run within a transaction, since the restart logic can
# fail the entire transaction.)
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 inmemory = False
Phil Cohen
rebase: rerun a rebase on-disk if IMM merge conflicts arise...
r35321
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 if inmemory:
Phil Cohen
rebase: rerun a rebase on-disk if IMM merge conflicts arise...
r35321 try:
# in-memory merge doesn't support conflicts, so if we hit any, abort
# and re-run as an on-disk merge.
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 return _origrebase(ui, repo, inmemory=inmemory, **opts)
Phil Cohen
rebase: rerun a rebase on-disk if IMM merge conflicts arise...
r35321 except error.InMemoryMergeConflictsError:
ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
' merge\n'))
_origrebase(ui, repo, **{'abort': True})
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 return _origrebase(ui, repo, inmemory=False, **opts)
Phil Cohen
rebase: rerun a rebase on-disk if IMM merge conflicts arise...
r35321 else:
return _origrebase(ui, repo, **opts)
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 def _origrebase(ui, repo, inmemory=False, **opts):
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 opts = pycompat.byteskwargs(opts)
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 rbsrt = rebaseruntime(repo, ui, inmemory, opts)
Matt Mackall
rebase: add --edit switch
r15219
Martin von Zweigbergk
rebase: use context manager for locking in rebase()
r32917 with repo.wlock(), repo.lock():
Stefano Tortarolo
Add rebase extension
r6906 # 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:
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 destmap = _definedestmap(ui, repo, rbsrt, destf, srcf, basef, revf,
destspace=destspace)
Jun Wu
rebase: change internal format to support destination map...
r34006 retcode = rbsrt._preparenewrebase(destmap)
Kostia Balytskyi
rebase: move new rebase preparation to be a method of the RR class...
r29473 if retcode is not None:
return retcode
Mads Kiilerich
rebase: tell when reopening a closed branch head...
r21027
Durham Goode
rebase: add config to move rebase into a single transaction...
r33569 tr = None
Durham Goode
rebase: use one dirstateguard for when using rebase.singletransaction...
r33621 dsguard = None
singletr = ui.configbool('rebase', 'singletransaction')
if singletr:
Durham Goode
rebase: add config to move rebase into a single transaction...
r33569 tr = repo.transaction('rebase')
Phil Cohen
rebase: don't take out a dirstate guard for in-memory rebase...
r35496
# If `rebase.singletransaction` is enabled, wrap the entire operation in
# one transaction here. Otherwise, transactions are obtained when
# committing each node, which is slower but allows partial success.
Durham Goode
rebase: add config to move rebase into a single transaction...
r33569 with util.acceptintervention(tr):
Phil Cohen
rebase: don't take out a dirstate guard for in-memory rebase...
r35496 # Same logic for the dirstate guard, except we don't create one when
# rebasing in-memory (it's not needed).
if singletr and not inmemory:
Durham Goode
rebase: use one dirstateguard for when using rebase.singletransaction...
r33621 dsguard = dirstateguard.dirstateguard(repo, 'rebase')
with util.acceptintervention(dsguard):
rbsrt._performrebase(tr)
Durham Goode
rebase: add config to move rebase into a single transaction...
r33569
Kostia Balytskyi
rebase: move rebase finish logic to be a method of the RR class...
r29478 rbsrt._finishrebase()
Stefano Tortarolo
Add rebase extension
r6906
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 def _definedestmap(ui, repo, rbsrt, destf=None, srcf=None, basef=None,
revf=None, destspace=None):
Jun Wu
rebase: change internal format to support destination map...
r34006 """use revisions argument to define destmap {srcrev: destrev}"""
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'))
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 if not rbsrt.inmemory:
Phil Cohen
rebase: do not bail on uncomitted changes if rebasing in-memory...
r35292 cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136
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'))
Jun Wu
rebase: initial support for multiple destinations...
r34007 dest = None
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136
if revf:
rebaseset = scmutil.revrange(repo, revf)
if not rebaseset:
ui.status(_('empty "rev" revision set - nothing to rebase\n'))
Jun Wu
rebase: change internal format to support destination map...
r34006 return None
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136 elif srcf:
src = scmutil.revrange(repo, [srcf])
if not src:
ui.status(_('empty "source" revision set - nothing to rebase\n'))
Jun Wu
rebase: change internal format to support destination map...
r34006 return None
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136 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"))
Jun Wu
rebase: change internal format to support destination map...
r34006 return None
Jun Wu
rebase: initial support for multiple destinations...
r34007 if destf:
# --base does not support multiple destinations
dest = scmutil.revsingle(repo, destf)
else:
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))
Jun Wu
rebase: change internal format to support destination map...
r34006 return None
Phil Cohen
rebase: disable `inmemory` if the rebaseset contains the working copy...
r35333 # If rebasing the working copy parent, force in-memory merge to be off.
#
# This is because the extra work of checking out the newly rebased commit
# outweights the benefits of rebasing in-memory, and executing an extra
# update command adds a bit of overhead, so better to just do it on disk. In
# all other cases leave it on.
#
# Note that there are cases where this isn't true -- e.g., rebasing large
# stacks that include the WCP. However, I'm not yet sure where the cutoff
# is.
rebasingwcp = repo['.'].rev() in rebaseset
Phil Cohen
rebase: switch ui.log calls to common style...
r35503 ui.log("rebase", "", rebase_rebasing_wcp=rebasingwcp)
Phil Cohen
rebase: replace --inmemory flag with rebase.experimental.inmemory config...
r35389 if rbsrt.inmemory and rebasingwcp:
rbsrt.inmemory = False
Phil Cohen
rebase: disable `inmemory` if the rebaseset contains the working copy...
r35333 # Check these since we did not before.
cmdutil.checkunfinished(repo)
cmdutil.bailifchanged(repo)
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)
Jun Wu
rebase: initial support for multiple destinations...
r34007 allsrc = revsetlang.formatspec('%ld', rebaseset)
alias = {'ALLSRC': allsrc}
if dest is None:
try:
# fast path: try to resolve dest without SRC alias
dest = scmutil.revsingle(repo, destf, localalias=alias)
except error.RepoLookupError:
# multi-dest path: resolve dest for each SRC separately
destmap = {}
for r in rebaseset:
alias['SRC'] = revsetlang.formatspec('%d', r)
# use repo.anyrevs instead of scmutil.revsingle because we
# don't want to abort if destset is empty.
destset = repo.anyrevs([destf], user=True, localalias=alias)
size = len(destset)
if size == 1:
destmap[r] = destset.first()
elif size == 0:
ui.note(_('skipping %s - empty destination\n') % repo[r])
else:
raise error.Abort(_('rebase destination for %s is not '
'unique') % repo[r])
if dest is not None:
# single-dest case: assign dest to each rev in rebaseset
destrev = dest.rev()
destmap = {r: destrev for r in rebaseset} # {srcrev: destrev}
if not destmap:
ui.status(_('nothing to rebase - empty destination\n'))
return None
Jun Wu
rebase: change internal format to support destination map...
r34006
return destmap
Pierre-Yves David
rebase: extract rebaseset and destination computation in a function...
r28136
Martin von Zweigbergk
rebase: rename "target" to "dest" in variable names...
r32248 def externalparent(repo, state, destancestors):
Mads Kiilerich
rebase: refactor and rename checkexternal - it is a getter more than a setter
r19955 """Return the revision that should be used as the second parent
Martin von Zweigbergk
rebase: rename "target" to "dest" in variable names...
r32248 when the revisions in state is collapsed on top of destancestors.
Mads Kiilerich
rebase: refactor and rename checkexternal - it is a getter more than a setter
r19955 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
Martin von Zweigbergk
rebase: rename "target" to "dest" in variable names...
r32248 and p.rev() not in destancestors):
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') %
Martin von Zweigbergk
rebase: rename "target" to "dest" in variable names...
r32248 (max(destancestors),
Mads Kiilerich
rebase: improve error message for more than one external parent
r19956 ', '.join(str(p) for p in sorted(parents))))
Stefano Tortarolo
rebase: refactoring...
r10351
Phil Cohen
rebase: add concludememorynode(), and call it when rebasing in-memory...
r35320 def concludememorynode(repo, rev, p1, p2, wctx=None,
commitmsg=None, editor=None, extrafn=None,
keepbranches=False, date=None):
Phil Cohen
rebase: don't take out a dirstate guard for in-memory rebase...
r35496 '''Commit the memory changes with parents p1 and p2. Reuse commit info from
rev but also store useful information in extra.
Phil Cohen
rebase: add concludememorynode(), and call it when rebasing in-memory...
r35320 Return node of committed revision.'''
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)
destphase = max(ctx.phase(), phases.draft)
overrides = {('phases', 'new-commit'): destphase}
with repo.ui.configoverride(overrides, 'rebase'):
if keepbranch:
repo.ui.setconfig('ui', 'allowemptycommit', True)
# Replicates the empty check in ``repo.commit``.
if wctx.isempty() and not repo.ui.configbool('ui', 'allowemptycommit'):
return None
if date is None:
date = ctx.date()
# By convention, ``extra['branch']`` (set by extrafn) clobbers
# ``branch`` (used when passing ``--keepbranches``).
branch = repo[p1].branch()
if 'branch' in extra:
branch = extra['branch']
memctx = wctx.tomemctx(commitmsg, parents=(p1, p2), date=date,
extra=extra, user=ctx.user(), branch=branch, editor=editor)
commitres = repo.commitctx(memctx)
wctx.clean() # Might be reused
return commitres
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 when using rebase.singletransaction...
r33621 dsguard = util.nullcontextmanager()
if not repo.ui.configbool('rebase', 'singletransaction'):
dsguard = dirstateguard.dirstateguard(repo, 'rebase')
with dsguard:
rebase: backed out changeset 2519994d25ca...
r33135 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
Martin von Zweigbergk
merge with stable
r33140 destphase = max(ctx.phase(), phases.draft)
overrides = {('phases', 'new-commit'): destphase}
rebase: backed out changeset 2519994d25ca...
r33135 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
rebase: backed out changeset 2519994d25ca...
r33135 repo.dirstate.setbranch(repo[newnode].branch())
return newnode
Stefano Tortarolo
Add rebase extension
r6906
Phil Cohen
rebase: pass wctx to rebasenode()...
r35317 def rebasenode(repo, rev, p1, base, state, collapse, dest, wctx):
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 'Rebase a single revision rev on top of p1 using base as merge ancestor'
Stefano Tortarolo
Add rebase extension
r6906 # Merge phase
Martin von Zweigbergk
rebase: rename "target" to "dest" in variable names...
r32248 # Update to destination and merge it with local
Phil Cohen
rebase: do not update if IMM; instead, set the overlaywctx's parents...
r35318 if wctx.isinmemory():
wctx.setbase(repo[p1])
Stefano Tortarolo
Add rebase extension
r6906 else:
Phil Cohen
rebase: do not update if IMM; instead, set the overlaywctx's parents...
r35318 if repo['.'].rev() != p1:
repo.ui.debug(" update to %d:%s\n" % (p1, repo[p1]))
mergemod.update(repo, p1, False, True)
else:
repo.ui.debug(" already in destination\n")
Phil Cohen
rebase: fix for hgsubversion...
r35411 # This is, alas, necessary to invalidate workingctx's manifest cache,
# as well as other data we litter on it in other places.
wctx = repo[None]
Phil Cohen
rebase: do not update if IMM; instead, set the overlaywctx's parents...
r35318 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,
Phil Cohen
rebase: pass the wctx object (IMM or on-disk) to merge.update...
r35319 labels=['dest', 'source'], wc=wctx)
Matt Mackall
rebase: move duplicatecopies next to merge...
r22905 if collapse:
Phil Cohen
context: add workingfilectx.markcopied...
r34788 copies.duplicatecopies(repo, wctx, rev, dest)
Matt Mackall
rebase: move duplicatecopies next to merge...
r22905 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()
Phil Cohen
context: add workingfilectx.markcopied...
r34788 copies.duplicatecopies(repo, wctx, rev, p1rev, skiprev=dest)
Matt Mackall
rebase: move duplicatecopies next to merge...
r22905 return stats
Dirkjan Ochtman
strip trailing whitespace, replace tabs by spaces
r6923
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 def adjustdest(repo, rev, destmap, state, skipped):
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591 """adjust rebase destination given the current rebase state
rev is what is being rebased. Return a list of two revs, which are the
adjusted destinations for rev's p1 and p2, respectively. If a parent is
nullrev, return dest without adjustment for it.
Jun Wu
rebase: initial support for multiple destinations...
r34007 For example, when doing rebasing B+E to F, C to G, rebase will first move B
to B1, and E's destination will be adjusted from F to B1.
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591
B1 <- written during rebasing B
|
F <- original destination of B, E
|
| E <- rev, which is being rebased
| |
| D <- prev, one parent of rev being checked
| |
| x <- skipped, ex. no successor or successor in (::dest)
| |
Jun Wu
rebase: initial support for multiple destinations...
r34007 | C <- rebased as C', different destination
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591 | |
Jun Wu
rebase: initial support for multiple destinations...
r34007 | B <- rebased as B1 C'
|/ |
A G <- destination of C, different
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591
Another example about merge changeset, rebase -r C+G+H -d K, rebase will
first move C to C1, G to G1, and when it's checking H, the adjusted
destinations will be [C1, G1].
H C1 G1
/| | /
F G |/
K | | -> K
| C D |
| |/ |
| B | ...
|/ |/
A A
Jun Wu
rebase: sort destmap topologically...
r34008
Besides, adjust dest according to existing rebase information. For example,
B C D B needs to be rebased on top of C, C needs to be rebased on top
\|/ of D. We will rebase C first.
A
C' After rebasing C, when considering B's destination, use C'
| instead of the original C.
B D
\ /
A
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591 """
Jun Wu
rebase: change internal format to support destination map...
r34006 # pick already rebased revs with same dest from state as interesting source
dest = destmap[rev]
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 source = [s for s, d in state.items()
if d > 0 and destmap[s] == dest and s not in skipped]
Jun Wu
rebase: optimize "source" calculation in adjustdest...
r33848
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591 result = []
for prev in repo.changelog.parentrevs(rev):
adjusted = dest
if prev != nullrev:
candidate = repo.revs('max(%ld and (::%d))', source, prev).first()
if candidate is not None:
adjusted = state[candidate]
Jun Wu
rebase: sort destmap topologically...
r34008 if adjusted == dest and dest in state:
adjusted = state[dest]
if adjusted == revtodo:
# sortsource should produce an order that makes this impossible
raise error.ProgrammingError(
'rev %d should be rebased already at this time' % dest)
Jun Wu
rebase: move bookmark to destination for commits becoming empty (issue5627)...
r33591 result.append(adjusted)
return result
Jun Wu
rebase: remove rebaseset from _checkobsrebase...
r33845 def _checkobsrebase(repo, ui, rebaseobsrevs, 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
`rebaseobsskipped`: set of revisions from source skipped because they have
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 successors in destination or no non-obsolete successor.
Laurent Charignon
rebase: refactor of error handling code path for rebaseskipobsolete...
r28685 """
# Obsolete node with successors not in dest leads to divergence
divergenceok = ui.configbool('experimental',
Boris Feld
config: gather allowdivergence under the evolution namespace...
r34873 'evolution.allowdivergence')
Laurent Charignon
rebase: refactor of error handling code path for rebaseskipobsolete...
r28685 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 "
Boris Feld
config: gather allowdivergence under the evolution namespace...
r34873 "experimental.evolution.allowdivergence=True")
Laurent Charignon
rebase: refactor of error handling code path for rebaseskipobsolete...
r28685 raise error.Abort(msg % (",".join(divhashes),), hint=h)
Jun Wu
rebase: remove unnecessary '.unfiltered()' calls...
r34097 def successorrevs(unfi, rev):
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 """yield revision numbers for successors of rev"""
Jun Wu
rebase: remove unnecessary '.unfiltered()' calls...
r34097 assert unfi.filtername is None
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 nodemap = unfi.changelog.nodemap
for s in obsutil.allsuccessors(unfi.obsstore, [unfi[rev].node()]):
if s in nodemap:
yield nodemap[s]
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 def defineparents(repo, rev, destmap, state, skipped, obsskipped):
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 """Return new parents and optionally a merge base for rev being rebased
The destination specified by "dest" cannot always be used directly because
previously rebase result could affect destination. For example,
Stefano Tortarolo
Add rebase extension
r6906
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 D E rebase -r C+D+E -d B
|/ C will be rebased to C'
B C D's new destination will be C' instead of B
|/ E's new destination will be C' instead of B
A
Stefano Tortarolo
Add rebase extension
r6906
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 The new parents of a merge is slightly more complicated. See the comment
block below.
"""
Jun Wu
rebase: do not crash rebasing merge with a parent having hidden successor...
r34094 # use unfiltered changelog since successorrevs may return filtered nodes
Jun Wu
rebase: remove unnecessary '.unfiltered()' calls...
r34097 assert repo.filtername is None
cl = repo.changelog
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 def isancestor(a, b):
# take revision numbers instead of nodes
if a == b:
return True
elif a > b:
return False
return cl.isancestor(cl.node(a), cl.node(b))
Jun Wu
rebase: change internal format to support destination map...
r34006 dest = destmap[rev]
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 oldps = repo.changelog.parentrevs(rev) # old parents
newps = [nullrev, nullrev] # new parents
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 dests = adjustdest(repo, rev, destmap, state, skipped)
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 bases = list(oldps) # merge base candidates, initially just old parents
Stefano Tortarolo
Add rebase extension
r6906
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 if all(r == nullrev for r in oldps[1:]):
# For non-merge changeset, just move p to adjusted dest as requested.
newps[0] = dests[0]
else:
# For merge changeset, if we move p to dests[i] unconditionally, both
# parents may change and the end result looks like "the merge loses a
# parent", which is a surprise. This is a limit because "--dest" only
# accepts one dest per src.
#
# Therefore, only move p with reasonable conditions (in this order):
# 1. use dest, if dest is a descendent of (p or one of p's successors)
# 2. use p's rebased result, if p is rebased (state[p] > 0)
#
# Comparing with adjustdest, the logic here does some additional work:
# 1. decide which parents will not be moved towards dest
# 2. if the above decision is "no", should a parent still be moved
# because it was rebased?
#
# For example:
#
# C # "rebase -r C -d D" is an error since none of the parents
# /| # can be moved. "rebase -r B+C -d D" will move C's parent
# A B D # B (using rule "2."), since B will be rebased.
#
# The loop tries to be not rely on the fact that a Mercurial node has
# at most 2 parents.
for i, p in enumerate(oldps):
np = p # new parent
if any(isancestor(x, dests[i]) for x in successorrevs(repo, p)):
np = dests[i]
elif p in state and state[p] > 0:
np = state[p]
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 # "bases" only record "special" merge bases that cannot be
# calculated from changelog DAG (i.e. isancestor(p, np) is False).
# For example:
#
# B' # rebase -s B -d D, when B was rebased to B'. dest for C
# | C # is B', but merge base for C is B, instead of
# D | # changelog.ancestor(C, B') == A. If changelog DAG and
# | B # "state" edges are merged (so there will be an edge from
# |/ # B to B'), the merge base is still ancestor(C, B') in
# A # the merged graph.
#
# Also see https://bz.mercurial-scm.org/show_bug.cgi?id=1950#c8
# which uses "virtual null merge" to explain this situation.
if isancestor(p, np):
bases[i] = nullrev
# If one parent becomes an ancestor of the other, drop the ancestor
for j, x in enumerate(newps[:i]):
if x == nullrev:
continue
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 if isancestor(np, x): # CASE-1
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 np = nullrev
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 elif isancestor(x, np): # CASE-2
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 newps[j] = np
np = nullrev
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 # New parents forming an ancestor relationship does not
# mean the old parents have a similar relationship. Do not
# set bases[x] to nullrev.
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 bases[j], bases[i] = bases[i], bases[j]
newps[i] = np
# "rebasenode" updates to new p1, and the old p1 will be used as merge
# base. If only p2 changes, merging using unchanged p1 as merge base is
# suboptimal. Therefore swap parents to make the merge sane.
if newps[1] != nullrev and oldps[0] == newps[0]:
assert len(newps) == 2 and len(oldps) == 2
newps.reverse()
bases.reverse()
# No parent change might be an error because we fail to make rev a
# descendent of requested dest. This can happen, for example:
#
# C # rebase -r C -d D
# /| # None of A and B will be changed to D and rebase fails.
# A B D
if set(newps) == set(oldps) and dest not in newps:
Jun Wu
rebase: change "result would have 3 parent" error message (BC)...
r33786 raise error.Abort(_('cannot rebase %d:%s without '
'moving at least one of its parents')
% (rev, repo[rev]))
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783
Jun Wu
rebase: sort destmap topologically...
r34008 # Source should not be ancestor of dest. The check here guarantees it's
# impossible. With multi-dest, the initial check does not cover complex
# cases since we don't have abstractions to dry-run rebase cheaply.
if any(p != nullrev and isancestor(rev, p) for p in newps):
raise error.Abort(_('source is ancestor of destination'))
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783 # "rebasenode" updates to new p1, use the corresponding merge base.
if bases[0] != nullrev:
base = bases[0]
else:
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484 base = None
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783
# Check if the merge will contain unwanted changes. That may happen if
# there are multiple special (non-changelog ancestor) merge bases, which
# cannot be handled well by the 3-way merge algorithm. For example:
#
# F
# /|
# D E # "rebase -r D+E+F -d Z", when rebasing F, if "D" was chosen
# | | # as merge base, the difference between D and F will include
# B C # C, so the rebased F will contain C surprisingly. If "E" was
# |/ # chosen, the rebased F will contain B.
# A Z
#
# But our merge base candidates (D and E in above case) could still be
# better than the default (ancestor(F, Z) == null). Therefore still
# pick one (so choose p1 above).
if sum(1 for b in bases if b != nullrev) > 1:
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 unwanted = [None, None] # unwanted[i]: unwanted revs if choose bases[i]
for i, base in enumerate(bases):
if base == nullrev:
continue
# Revisions in the side (not chosen as merge base) branch that
# might contain "surprising" contents
siderevs = list(repo.revs('((%ld-%d) %% (%d+%d))',
bases, base, base, dest))
Mads Kiilerich
rebase: move base calculation from rebasenode() to defineparents()...
r23484
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 # If those revisions are covered by rebaseset, the result is good.
# A merge in rebaseset would be considered to cover its ancestors.
if siderevs:
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 rebaseset = [r for r, d in state.items()
if d > 0 and r not in obsskipped]
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 merges = [r for r in rebaseset
if cl.parentrevs(r)[1] != nullrev]
unwanted[i] = list(repo.revs('%ld - (::%ld) - %ld',
siderevs, merges, rebaseset))
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 # Choose a merge base that has a minimal number of unwanted revs.
l, i = min((len(revs), i)
for i, revs in enumerate(unwanted) if revs is not None)
base = bases[i]
# newps[0] should match merge base if possible. Currently, if newps[i]
# is nullrev, the only case is newps[i] and newps[j] (j < i), one is
# the other's ancestor. In that case, it's fine to not swap newps here.
# (see CASE-1 and CASE-2 above)
if i != 0 and newps[i] != nullrev:
newps[0], newps[i] = newps[i], newps[0]
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783
Jun Wu
rebase: choose merge base without unwanted revisions...
r33863 # The merge will include unwanted revisions. Abort now. Revisit this if
# we have a more advanced merge algorithm that handles multiple bases.
if l > 0:
unwanteddesc = _(' or ').join(
(', '.join('%d:%s' % (r, repo[r]) for r in revs)
for revs in unwanted if revs is not None))
raise error.Abort(
_('rebasing %d:%s will include unwanted changes from %s')
% (rev, repo[rev], unwanteddesc))
repo.ui.debug(" future parents are %d and %d\n" % tuple(newps))
Jun Wu
rebase: rewrite core algorithm (issue5578) (issue5630)...
r33783
return newps[0], newps[1], 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
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'
Jun Wu
rebase: clean up rebasestate from active transaction...
r33056 # Make sure the active transaction won't write the state file
tr = repo.currenttransaction()
if tr:
tr.removefilegenerator('rebasestate')
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
Jun Wu
rebase: change internal format to support destination map...
r34006 def abort(repo, originalwd, destmap, state, activebookmark=None):
Tony Tung
rebase: restore bookmark state on abort...
r24758 '''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,
Martin von Zweigbergk
rebase: rename "target" to "dest" in variable names...
r32248 # their values within the state mapping will be the dest rev id. The
# dstates list must must not contain the dest rev (issue4896)
Jun Wu
rebase: change internal format to support destination map...
r34006 dstates = [s for r, s in state.items() if s >= 0 and s != destmap[r]]
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 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):
Martin von Zweigbergk
rebase: rename "target" to "destination" in messages...
r32249 repo.ui.warn(_("warning: new changesets detected on destination "
"branch, can't strip\n"))
Christian Delahousse
rebase: on abort delete rebase state file no matter what...
r26744 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
Jun Wu
rebase: change internal format to support destination map...
r34006 rebased = [s for r, s in state.items()
if s >= 0 and s != destmap[r]]
timeless
rebase: update working directory when aborting (issue5084)
r27988 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)
Jun Wu
rebase: change internal format to support destination map...
r34006 updateifonnodes.update(destmap.values())
Durham Goode
rebase: clear updatestate during rebase --abort in more cases...
r31222 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
Jun Wu
rebase: sort destmap topologically...
r34008 def sortsource(destmap):
"""yield source revisions in an order that we only rebase things once
If source and destination overlaps, we should filter out revisions
depending on other revisions which hasn't been rebased yet.
Yield a sorted list of revisions each time.
For example, when rebasing A to B, B to C. This function yields [B], then
[A], indicating B needs to be rebased first.
Raise if there is a cycle so the rebase is impossible.
"""
srcset = set(destmap)
while srcset:
srclist = sorted(srcset)
result = []
for r in srclist:
if destmap[r] not in srcset:
result.append(r)
if not result:
raise error.Abort(_('source and destination form a cycle'))
srcset -= set(result)
yield result
Jun Wu
rebase: move working parent and bookmark for obsoleted revs (BC)...
r34010 def buildstate(repo, destmap, collapse):
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 '''Define which revisions are going to be rebased and where
Stefano Tortarolo
Add rebase extension
r6906
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 repo: repo
Jun Wu
rebase: change internal format to support destination map...
r34006 destmap: {srcrev: destrev}
Pierre-Yves David
rebase: do not add second parent to rebased changeset (drop detach option) (BC)...
r17005 '''
Jun Wu
rebase: change internal format to support destination map...
r34006 rebaseset = destmap.keys()
Martin von Zweigbergk
rebase: unhide original working directory node as well (issue5219)...
r31297 originalwd = repo['.'].rev()
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.
Jun Wu
rebase: change internal format to support destination map...
r34006 if 'qtip' in repo.tags():
mqapplied = set(repo[s.node].rev() for s in repo.mq.applied)
if set(destmap.values()) & mqapplied:
raise error.Abort(_('cannot rebase onto an applied mq patch'))
Greg Ward
rebase: always check if rebasing onto an applied mq patch....
r10672
Jun Wu
rebase: sort destmap topologically...
r34008 # Get "cycle" error early by exhausting the generator.
sortedsrc = list(sortsource(destmap)) # a list of sorted revs
if not sortedsrc:
raise error.Abort(_('no matching revisions'))
# Only check the first batch of revisions to rebase not depending on other
# rebaseset. This means "source is ancestor of destination" for the second
# (and following) batches of revisions are not checked here. We rely on
# "defineparents" to do that check.
roots = list(repo.set('roots(%ld)', sortedsrc[0]))
Pierre-Yves David
rebase: use revset as soon as possible in internal logic...
r15267 if not roots:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('no matching revisions'))
Augie Fackler
rebase: sort roots by revision...
r36285 def revof(r):
return r.rev()
roots = sorted(roots, key=revof)
Martin von Zweigbergk
rebase: don't update state dict same way for each root...
r32175 state = dict.fromkeys(rebaseset, revtodo)
Jun Wu
rebase: sort destmap topologically...
r34008 emptyrebase = (len(sortedsrc) == 1)
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 for root in roots:
Jun Wu
rebase: change internal format to support destination map...
r34006 dest = repo[destmap[root.rev()]]
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 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()
Martin von Zweigbergk
rebase: rewrite "x in y.children()" as "y in x.parents()"...
r32900 if not collapse and samebranch and dest in root.parents():
Martin von Zweigbergk
rebase: allow rebase even if some revisions need no rebase (BC) (issue5422)...
r32272 # mark the revision as done by setting its new revision
# equal to its old (current) revisions
state[root.rev()] = root.rev()
Pierre-Yves David
rebase: support multiple roots for rebaseset...
r18424 repo.ui.debug('source is a child of destination\n')
Martin von Zweigbergk
rebase: allow rebase even if some revisions need no rebase (BC) (issue5422)...
r32272 continue
Stefano Tortarolo
Add rebase extension
r6906
Martin von Zweigbergk
rebase: allow rebase even if some revisions need no rebase (BC) (issue5422)...
r32272 emptyrebase = False
Martin von Zweigbergk
rebase: make debug logging more consistent...
r29936 repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root))
Martin von Zweigbergk
rebase: allow rebase even if some revisions need no rebase (BC) (issue5422)...
r32272 if emptyrebase:
return None
for rev in sorted(state):
parents = [p for p in repo.changelog.parentrevs(rev) if p != nullrev]
# if all parents of this revision are done, then so is this revision
if parents and all((state.get(p) == p for p in parents)):
state[rev] = rev
Jun Wu
rebase: change internal format to support destination map...
r34006 return originalwd, destmap, state
Stefano Tortarolo
Add rebase extension
r6906
Martin von Zweigbergk
merge with stable
r34366 def clearrebased(ui, repo, destmap, state, skipped, collapsedas=None,
Pulkit Goyal
rebase: add support to output nodechanges...
r34884 keepf=False, fm=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
Jun Wu
rebase: move bookmarks with --keep (issue5682)...
r34364 `collapsedas` node.
If `keepf` is not True, the rebase has --keep set and no nodes should be
removed (but bookmarks still need to be moved).
"""
Jun Wu
rebase: use scmutil.cleanupnodes (issue5606) (BC)...
r33332 tonode = repo.changelog.node
Jun Wu
rebase: move bookmarks with --keep (issue5682)...
r34364 replacements = {}
moves = {}
Jun Wu
rebase: remove "if True"...
r33333 for rev, newrev in sorted(state.items()):
if newrev >= 0 and newrev != rev:
Jun Wu
rebase: move bookmarks with --keep (issue5682)...
r34364 oldnode = tonode(rev)
newnode = collapsedas or tonode(newrev)
moves[oldnode] = newnode
if not keepf:
if rev in skipped:
succs = ()
else:
succs = (newnode,)
replacements[oldnode] = succs
scmutil.cleanupnodes(repo, replacements, 'rebase', moves)
Pulkit Goyal
rebase: add support to output nodechanges...
r34884 if fm:
Pulkit Goyal
rebase: use fm.formatlist() and fm.formatdict() to support user template...
r35123 hf = fm.hexfunc
fl = fm.formatlist
fd = fm.formatdict
nodechanges = fd({hf(oldn): fl([hf(n) for n in newn], name='node')
for oldn, newn in replacements.iteritems()},
key="oldnode", value="newnodes")
Pulkit Goyal
rebase: add support to output nodechanges...
r34884 fm.data(nodechanges=nodechanges)
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
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 if opts.get(r'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)
Martin von Zweigbergk
rebase: use context manager for locking in pullrebase()
r32918 with repo.wlock(), repo.lock():
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 if opts.get(r'update'):
del opts[r'update']
Pierre-Yves David
rebase: lock the repo during the full rebase operation...
r26029 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
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 if r'rev' in opts:
del opts[r'rev']
Pierre-Yves David
rebase: lock the repo during the full rebase operation...
r26029 # positional argument from pull conflicts with rebase's own
# --source.
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 if r'source' in opts:
del opts[r'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))
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 opts[r'_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)
Stefano Tortarolo
Add rebase extension
r6906 else:
Pulkit Goyal
py3: handle keyword arguments in hgext/rebase.py...
r35003 if opts.get(r'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
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())
Jun Wu
rebase: change internal format to support destination map...
r34006 def _computeobsoletenotrebased(repo, rebaseobsrevs, destmap):
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 """Return (obsoletenotrebased, obsoletewithoutsuccessorindestination).
`obsoletenotrebased` is a mapping mapping obsolete => successor for all
obsolete nodes to be rebased given in `rebaseobsrevs`.
Laurent Charignon
rebase: don't rebase obsolete commits with no successor...
r27012
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 `obsoletewithoutsuccessorindestination` is a set with obsolete revisions
without a successor in destination.
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013
`obsoleteextinctsuccessors` is a set of obsolete revisions with only
obsolete successors.
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 """
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349 obsoletenotrebased = {}
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 obsoletewithoutsuccessorindestination = set([])
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 obsoleteextinctsuccessors = set([])
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349
Jun Wu
rebase: remove unnecessary '.unfiltered()' calls...
r34097 assert repo.filtername is None
Pierre-Yves David
rebase: use a direct reference to repo.changelog...
r26674 cl = repo.changelog
Jun Wu
rebase: rewrite _computeobsoletenotrebased...
r34005 nodemap = cl.nodemap
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 extinctnodes = set(cl.node(r) for r in repo.revs('extinct()'))
Jun Wu
rebase: rewrite _computeobsoletenotrebased...
r34005 for srcrev in rebaseobsrevs:
srcnode = cl.node(srcrev)
Jun Wu
rebase: change internal format to support destination map...
r34006 destnode = cl.node(destmap[srcrev])
Jun Wu
rebase: rewrite _computeobsoletenotrebased...
r34005 # XXX: more advanced APIs are required to handle split correctly
Denis Laxalde
rebase: make "successors" a set in _computeobsoletenotrebased()...
r36015 successors = set(obsutil.allsuccessors(repo.obsstore, [srcnode]))
Denis Laxalde
rebase: eliminate node from successors early in _computeobsoletenotrebased()
r36012 # obsutil.allsuccessors includes node itself
successors.remove(srcnode)
Denis Laxalde
rebase: make "successors" a set in _computeobsoletenotrebased()...
r36015 if successors.issubset(extinctnodes):
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 # all successors are extinct
obsoleteextinctsuccessors.add(srcrev)
Denis Laxalde
rebase: eliminate node from successors early in _computeobsoletenotrebased()
r36012 if not successors:
# no successor
Jun Wu
rebase: rewrite _computeobsoletenotrebased...
r34005 obsoletenotrebased[srcrev] = None
else:
for succnode in successors:
Denis Laxalde
rebase: eliminate node from successors early in _computeobsoletenotrebased()
r36012 if succnode not in nodemap:
Jun Wu
rebase: rewrite _computeobsoletenotrebased...
r34005 continue
if cl.isancestor(succnode, destnode):
obsoletenotrebased[srcrev] = nodemap[succnode]
break
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 else:
# If 'srcrev' has a successor in rebase set but none in
# destination (which would be catched above), we shall skip it
# and its descendants to avoid divergence.
Denis Laxalde
rebase: eliminate node from successors early in _computeobsoletenotrebased()
r36012 if any(nodemap[s] in destmap for s in successors):
Denis Laxalde
rebase: exclude descendants of obsoletes w/o a successor in dest (issue5300)...
r35049 obsoletewithoutsuccessorindestination.add(srcrev)
Laurent Charignon
rebase: don't rebase obsolete commits with no successor...
r27012
Denis Laxalde
rebase: do not consider extincts for divergence detection (issue5782)...
r36013 return (
obsoletenotrebased,
obsoletewithoutsuccessorindestination,
obsoleteextinctsuccessors,
)
Laurent Charignon
rebase: don't rebase obsolete commit whose successor is already rebased...
r26349
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')])