Newer
Older
# rebase.py - rebasing feature for mercurial
#
# Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
'''command to move sets of revisions to a different ancestor
This extension lets you rebase changesets in an existing Mercurial
repository.
https://mercurial-scm.org/wiki/RebaseExtension
from __future__ import absolute_import
import errno
import os
from mercurial.i18n import _
from mercurial.node import (
hex,
nullid,
nullrev,
short,
)
from mercurial import (
bookmarks,
cmdutil,
commands,
copies,
destutil,
error,
extensions,
hg,
lock,
patch,
phases,
registrar,
repair,
repoview,
revset,
scmutil,
util,
)
release = lock.release
templateopts = cmdutil.templateopts
# 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
Stefano Tortarolo
committed
nullmerge = -2
revignored = -3
revprecursor = -4
revpruned = -5
revskipped = (revignored, revprecursor, revpruned)
Stefano Tortarolo
committed
command = registrar.command(cmdtable)
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
# 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.
testedwith = 'ships-with-hg-core'
def _nothingtorebase():
return 1
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()
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
committed
def _destrebase(repo, sourceset, destspace=None):
"""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
committed
onheadcheck=False, destspace=destspace)
revsetpredicate = registrar.revsetpredicate()
@revsetpredicate('_destrebase')
def _revsetdestrebase(repo, subset, x):
# ``_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
sourceset = None
if x is not None:
sourceset = revset.getset(repo, smartset.fullreposet(repo), x)
return subset & smartset.baseset([_destrebase(repo, sourceset)])
class rebaseruntime(object):
"""This class is a container for rebase runtime state"""
def __init__(self, repo, ui, opts=None):
if opts is None:
opts = {}
self.repo = repo
self.ui = ui
self.opts = opts
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
self.dest = None
self.skipped = set()
self.destancestors = set()
self.collapsef = opts.get('collapse', False)
self.collapsemsg = cmdutil.logmessage(ui, opts)
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
committed
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)
self.obsoletenotrebased = {}
Kostia Balytskyi
committed
def storestatus(self, tr=None):
"""Store the current status to allow recovery"""
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):
repo = self.repo.unfiltered()
f.write(repo[self.originalwd].hex() + '\n')
f.write(repo[self.dest].hex() + '\n')
f.write(repo[self.external].hex() + '\n')
f.write('%d\n' % int(self.collapsef))
f.write('%d\n' % int(self.keepf))
f.write('%d\n' % int(self.keepbranchesf))
f.write('%s\n' % (self.activebookmark or ''))
for d, v in self.state.iteritems():
oldrev = repo[d].hex()
if v >= 0:
newrev = repo[v].hex()
elif v == revtodo:
# To maintain format compatibility, we have to use nullid.
# Please do remove this special case when upgrading the format.
newrev = hex(nullid)
else:
newrev = v
f.write("%s:%s\n" % (oldrev, newrev))
repo.ui.debug('rebase status stored\n')
def restorestatus(self):
"""Restore a previously stored status"""
repo = self.repo
keepbranches = None
collapse = False
external = nullrev
activebookmark = None
Loading
Loading full blame...