sapling/hgext/transplant.py

699 lines
26 KiB
Python
Raw Normal View History

2006-11-28 02:13:01 +03:00
# Patch transplanting extension for Mercurial
#
# Copyright 2006, 2007 Brendan Cully <brendan@kublai.com>
2006-11-28 02:13:01 +03:00
#
# This software may be used and distributed according to the terms of the
2010-01-20 07:20:08 +03:00
# GNU General Public License version 2 or any later version.
2006-11-28 02:13:01 +03:00
'''command to transplant changesets from another branch
2006-11-28 02:13:01 +03:00
2013-04-16 21:20:23 +04:00
This extension allows you to transplant changes to another parent revision,
possibly in another repository. The transplant is done using 'diff' patches.
2006-11-28 02:13:01 +03:00
Transplanted patches are recorded in .hg/transplant/transplants, as a
map from a changeset hash to its hash in the source repository.
2006-11-28 02:13:01 +03:00
'''
from mercurial.i18n import _
import os, tempfile
2011-05-22 00:01:28 +04:00
from mercurial.node import short
from mercurial import bundlerepo, hg, merge, match
from mercurial import patch, revlog, scmutil, util, error, cmdutil
from mercurial import revset, templatekw, exchange
class TransplantError(error.Abort):
pass
cmdtable = {}
command = cmdutil.command(cmdtable)
testedwith = 'internal'
2009-06-10 17:10:21 +04:00
class transplantentry(object):
2006-11-28 02:13:01 +03:00
def __init__(self, lnode, rnode):
self.lnode = lnode
self.rnode = rnode
2009-06-10 17:10:21 +04:00
class transplants(object):
2006-11-28 02:13:01 +03:00
def __init__(self, path=None, transplantfile=None, opener=None):
self.path = path
self.transplantfile = transplantfile
self.opener = opener
if not opener:
2011-04-20 21:54:57 +04:00
self.opener = scmutil.opener(self.path)
self.transplants = {}
2006-11-28 02:13:01 +03:00
self.dirty = False
self.read()
def read(self):
abspath = os.path.join(self.path, self.transplantfile)
if self.transplantfile and os.path.exists(abspath):
for line in self.opener.read(self.transplantfile).splitlines():
2006-11-28 02:13:01 +03:00
lnode, rnode = map(revlog.bin, line.split(':'))
list = self.transplants.setdefault(rnode, [])
list.append(transplantentry(lnode, rnode))
2006-11-28 02:13:01 +03:00
def write(self):
if self.dirty and self.transplantfile:
if not os.path.isdir(self.path):
os.mkdir(self.path)
fp = self.opener(self.transplantfile, 'w')
for list in self.transplants.itervalues():
for t in list:
l, r = map(revlog.hex, (t.lnode, t.rnode))
fp.write(l + ':' + r + '\n')
2006-11-28 02:13:01 +03:00
fp.close()
self.dirty = False
def get(self, rnode):
return self.transplants.get(rnode) or []
2006-11-28 02:13:01 +03:00
def set(self, lnode, rnode):
list = self.transplants.setdefault(rnode, [])
list.append(transplantentry(lnode, rnode))
2006-11-28 02:13:01 +03:00
self.dirty = True
def remove(self, transplant):
list = self.transplants.get(transplant.rnode)
if list:
del list[list.index(transplant)]
self.dirty = True
2006-11-28 02:13:01 +03:00
2009-06-10 17:10:21 +04:00
class transplanter(object):
def __init__(self, ui, repo, opts):
2006-11-28 02:13:01 +03:00
self.ui = ui
self.path = repo.join('transplant')
2011-04-20 21:54:57 +04:00
self.opener = scmutil.opener(self.path)
2009-02-09 02:14:07 +03:00
self.transplants = transplants(self.path, 'transplants',
opener=self.opener)
def getcommiteditor():
editform = cmdutil.mergeeditform(repo[None], 'transplant')
return cmdutil.getcommiteditor(editform=editform, **opts)
self.getcommiteditor = getcommiteditor
2006-11-28 02:13:01 +03:00
def applied(self, repo, node, parent):
'''returns True if a node is already an ancestor of parent
or is parent or has already been transplanted'''
if hasnode(repo, parent):
parentrev = repo.changelog.rev(parent)
2006-11-28 02:13:01 +03:00
if hasnode(repo, node):
rev = repo.changelog.rev(node)
reachable = repo.changelog.ancestors([parentrev], rev,
inclusive=True)
if rev in reachable:
2006-11-28 02:13:01 +03:00
return True
for t in self.transplants.get(node):
# it might have been stripped
if not hasnode(repo, t.lnode):
self.transplants.remove(t)
return False
lnoderev = repo.changelog.rev(t.lnode)
if lnoderev in repo.changelog.ancestors([parentrev], lnoderev,
inclusive=True):
2006-11-28 02:13:01 +03:00
return True
return False
def apply(self, repo, source, revmap, merges, opts={}):
'''apply the revisions in revmap one by one in revision order'''
revs = sorted(revmap)
2006-11-28 02:13:01 +03:00
p1, p2 = repo.dirstate.parents()
pulls = []
diffopts = patch.difffeatureopts(self.ui, opts)
2006-11-28 02:13:01 +03:00
diffopts.git = True
lock = wlock = tr = None
2006-11-28 02:13:01 +03:00
try:
wlock = repo.wlock()
lock = repo.lock()
tr = repo.transaction('transplant')
2006-11-28 02:13:01 +03:00
for rev in revs:
node = revmap[rev]
2011-05-22 00:01:28 +04:00
revstr = '%s:%s' % (rev, short(node))
2006-11-28 02:13:01 +03:00
if self.applied(repo, node, p1):
self.ui.warn(_('skipping already applied revision %s\n') %
revstr)
continue
parents = source.changelog.parents(node)
if not (opts.get('filter') or opts.get('log')):
2009-02-09 02:14:07 +03:00
# If the changeset parent is the same as the
# wdir's parent, just pull it.
2006-11-28 02:13:01 +03:00
if parents[0] == p1:
pulls.append(node)
p1 = node
continue
if pulls:
if source != repo:
exchange.pull(repo, source.peer(), heads=pulls)
merge.update(repo, pulls[-1], False, False, None)
2006-11-28 02:13:01 +03:00
p1, p2 = repo.dirstate.parents()
pulls = []
domerge = False
if node in merges:
2009-02-09 02:14:07 +03:00
# pulling all the merge revs at once would mean we
# couldn't transplant after the latest even if
# transplants before them fail.
2006-11-28 02:13:01 +03:00
domerge = True
if not hasnode(repo, node):
exchange.pull(repo, source.peer(), heads=[node])
2006-11-28 02:13:01 +03:00
skipmerge = False
2006-11-28 02:13:01 +03:00
if parents[1] != revlog.nullid:
if not opts.get('parent'):
self.ui.note(_('skipping merge changeset %s:%s\n')
% (rev, short(node)))
skipmerge = True
else:
parent = source.lookup(opts['parent'])
if parent not in parents:
raise util.Abort(_('%s is not a parent of %s') %
(short(parent), short(node)))
else:
parent = parents[0]
if skipmerge:
2006-11-28 02:13:01 +03:00
patchfile = None
else:
fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
fp = os.fdopen(fd, 'w')
gen = patch.diff(source, parent, node, opts=diffopts)
for chunk in gen:
fp.write(chunk)
2006-11-28 02:13:01 +03:00
fp.close()
del revmap[rev]
if patchfile or domerge:
try:
try:
n = self.applyone(repo, node,
source.changelog.read(node),
patchfile, merge=domerge,
log=opts.get('log'),
filter=opts.get('filter'))
except TransplantError:
# Do not rollback, it is up to the user to
# fix the merge or cancel everything
tr.close()
raise
if n and domerge:
2006-11-28 02:13:01 +03:00
self.ui.status(_('%s merged at %s\n') % (revstr,
2011-05-22 00:01:28 +04:00
short(n)))
elif n:
2009-02-09 02:14:07 +03:00
self.ui.status(_('%s transplanted to %s\n')
2011-05-22 00:01:28 +04:00
% (short(node),
short(n)))
2006-11-28 02:13:01 +03:00
finally:
if patchfile:
os.unlink(patchfile)
tr.close()
2006-11-28 02:13:01 +03:00
if pulls:
exchange.pull(repo, source.peer(), heads=pulls)
merge.update(repo, pulls[-1], False, False, None)
2006-11-28 02:13:01 +03:00
finally:
self.saveseries(revmap, merges)
self.transplants.write()
if tr:
tr.release()
lock.release()
wlock.release()
2006-11-28 02:13:01 +03:00
def filter(self, filter, node, changelog, patchfile):
2006-11-28 02:13:01 +03:00
'''arbitrarily rewrite changeset before applying it'''
self.ui.status(_('filtering %s\n') % patchfile)
user, date, msg = (changelog[1], changelog[2], changelog[4])
fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
fp = os.fdopen(fd, 'w')
fp.write("# HG changeset patch\n")
fp.write("# User %s\n" % user)
fp.write("# Date %d %d\n" % date)
fp.write(msg + '\n')
fp.close()
try:
self.ui.system('%s %s %s' % (filter, util.shellquote(headerfile),
util.shellquote(patchfile)),
environ={'HGUSER': changelog[1],
'HGREVISION': revlog.hex(node),
},
onerr=util.Abort, errprefix=_('filter failed'))
user, date, msg = self.parselog(file(headerfile))[1:4]
finally:
os.unlink(headerfile)
return (user, date, msg)
2006-11-28 02:13:01 +03:00
def applyone(self, repo, node, cl, patchfile, merge=False, log=False,
filter=None):
2006-11-28 02:13:01 +03:00
'''apply the patch in patchfile to the repository as a transplant'''
(manifest, user, (time, timezone), files, message) = cl[:5]
date = "%d %d" % (time, timezone)
extra = {'transplant_source': node}
if filter:
(user, date, message) = self.filter(filter, node, cl, patchfile)
2006-11-28 02:13:01 +03:00
if log:
# we don't translate messages inserted into commits
2006-11-28 02:13:01 +03:00
message += '\n(transplanted from %s)' % revlog.hex(node)
2011-05-22 00:01:28 +04:00
self.ui.status(_('applying %s\n') % short(node))
2006-11-28 02:13:01 +03:00
self.ui.note('%s %s\n%s\n' % (user, date, message))
if not patchfile and not merge:
raise util.Abort(_('can only omit patchfile if merging'))
if patchfile:
try:
files = set()
patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
files = list(files)
2006-11-28 02:13:01 +03:00
except Exception, inst:
seriespath = os.path.join(self.path, 'series')
if os.path.exists(seriespath):
os.unlink(seriespath)
2011-04-05 01:21:59 +04:00
p1 = repo.dirstate.p1()
2006-11-28 02:13:01 +03:00
p2 = node
self.log(user, date, message, p1, p2, merge=merge)
2006-11-28 02:13:01 +03:00
self.ui.write(str(inst) + '\n')
raise TransplantError(_('fix up the merge and run '
'hg transplant --continue'))
2006-11-28 02:13:01 +03:00
else:
files = None
if merge:
p1, p2 = repo.dirstate.parents()
repo.setparents(p1, node)
m = match.always(repo.root, '')
else:
m = match.exact(repo.root, '', files)
2006-11-28 02:13:01 +03:00
2011-10-11 08:07:09 +04:00
n = repo.commit(message, user, date, extra=extra, match=m,
editor=self.getcommiteditor())
if not n:
self.ui.warn(_('skipping emptied changeset %s\n') % short(node))
return None
2006-11-28 02:13:01 +03:00
if not merge:
self.transplants.set(n, node)
return n
def resume(self, repo, source, opts):
2006-11-28 02:13:01 +03:00
'''recover last transaction and apply remaining changesets'''
if os.path.exists(os.path.join(self.path, 'journal')):
n, node = self.recover(repo, source, opts)
if n:
self.ui.status(_('%s transplanted as %s\n') % (short(node),
short(n)))
else:
self.ui.status(_('%s skipped due to empty diff\n')
% (short(node),))
2006-11-28 02:13:01 +03:00
seriespath = os.path.join(self.path, 'series')
if not os.path.exists(seriespath):
self.transplants.write()
2006-11-28 02:13:01 +03:00
return
nodes, merges = self.readseries()
revmap = {}
for n in nodes:
revmap[source.changelog.rev(n)] = n
os.unlink(seriespath)
self.apply(repo, source, revmap, merges, opts)
def recover(self, repo, source, opts):
2006-11-28 02:13:01 +03:00
'''commit working directory using journal metadata'''
node, user, date, message, parents = self.readlog()
merge = False
2006-11-28 02:13:01 +03:00
if not user or not date or not message or not parents[0]:
raise util.Abort(_('transplant log file is corrupt'))
parent = parents[0]
if len(parents) > 1:
if opts.get('parent'):
parent = source.lookup(opts['parent'])
if parent not in parents:
raise util.Abort(_('%s is not a parent of %s') %
(short(parent), short(node)))
else:
merge = True
extra = {'transplant_source': node}
2006-11-28 02:13:01 +03:00
wlock = repo.wlock()
try:
p1, p2 = repo.dirstate.parents()
if p1 != parent:
raise util.Abort(_('working directory not at transplant '
'parent %s') % revlog.hex(parent))
if merge:
repo.setparents(p1, parents[1])
modified, added, removed, deleted = repo.status()[:4]
if merge or modified or added or removed or deleted:
n = repo.commit(message, user, date, extra=extra,
editor=self.getcommiteditor())
if not n:
raise util.Abort(_('commit failed'))
if not merge:
self.transplants.set(n, node)
else:
n = None
self.unlog()
return n, node
finally:
wlock.release()
2006-11-28 02:13:01 +03:00
def readseries(self):
nodes = []
merges = []
cur = nodes
for line in self.opener.read('series').splitlines():
2006-11-28 02:13:01 +03:00
if line.startswith('# Merges'):
cur = merges
continue
cur.append(revlog.bin(line))
return (nodes, merges)
def saveseries(self, revmap, merges):
if not revmap:
return
if not os.path.isdir(self.path):
os.mkdir(self.path)
series = self.opener('series', 'w')
for rev in sorted(revmap):
2006-11-28 02:13:01 +03:00
series.write(revlog.hex(revmap[rev]) + '\n')
if merges:
series.write('# Merges\n')
for m in merges:
series.write(revlog.hex(m) + '\n')
series.close()
def parselog(self, fp):
parents = []
message = []
node = revlog.nullid
inmsg = False
user = None
date = None
for line in fp.read().splitlines():
if inmsg:
message.append(line)
elif line.startswith('# User '):
user = line[7:]
elif line.startswith('# Date '):
date = line[7:]
elif line.startswith('# Node ID '):
node = revlog.bin(line[10:])
elif line.startswith('# Parent '):
parents.append(revlog.bin(line[9:]))
elif not line.startswith('# '):
inmsg = True
message.append(line)
if None in (user, date):
raise util.Abort(_("filter corrupted changeset (no user or date)"))
return (node, user, date, '\n'.join(message), parents)
def log(self, user, date, message, p1, p2, merge=False):
2006-11-28 02:13:01 +03:00
'''journal changelog metadata for later recover'''
if not os.path.isdir(self.path):
os.mkdir(self.path)
fp = self.opener('journal', 'w')
fp.write('# User %s\n' % user)
fp.write('# Date %s\n' % date)
2006-11-28 02:13:01 +03:00
fp.write('# Node ID %s\n' % revlog.hex(p2))
fp.write('# Parent ' + revlog.hex(p1) + '\n')
if merge:
fp.write('# Parent ' + revlog.hex(p2) + '\n')
fp.write(message.rstrip() + '\n')
2006-11-28 02:13:01 +03:00
fp.close()
def readlog(self):
return self.parselog(self.opener('journal'))
2006-11-28 02:13:01 +03:00
def unlog(self):
'''remove changelog journal'''
absdst = os.path.join(self.path, 'journal')
if os.path.exists(absdst):
os.unlink(absdst)
def transplantfilter(self, repo, source, root):
def matchfn(node):
if self.applied(repo, node, root):
return False
if source.changelog.parents(node)[1] != revlog.nullid:
return False
extra = source.changelog.read(node)[5]
cnode = extra.get('transplant_source')
if cnode and self.applied(repo, cnode, root):
return False
return True
return matchfn
def hasnode(repo, node):
try:
return repo.changelog.rev(node) is not None
except error.RevlogError:
2006-11-28 02:13:01 +03:00
return False
def browserevs(ui, repo, nodes, opts):
'''interactively transplant changesets'''
displayer = cmdutil.show_changeset(ui, repo, opts)
2006-11-28 02:13:01 +03:00
transplants = []
merges = []
prompt = _('apply changeset? [ynmpcq?]:'
'$$ &yes, transplant this changeset'
'$$ &no, skip this changeset'
'$$ &merge at this changeset'
'$$ show &patch'
'$$ &commit selected changesets'
'$$ &quit and cancel transplant'
'$$ &? (show this help)')
2006-11-28 02:13:01 +03:00
for node in nodes:
displayer.show(repo[node])
2006-11-28 02:13:01 +03:00
action = None
while not action:
action = 'ynmpcq?'[ui.promptchoice(prompt)]
2006-11-28 02:13:01 +03:00
if action == '?':
for c, t in ui.extractchoices(prompt)[1]:
ui.write('%s: %s\n' % (c, t))
2006-11-28 02:13:01 +03:00
action = None
elif action == 'p':
parent = repo.changelog.parents(node)[0]
for chunk in patch.diff(repo, parent, node):
ui.write(chunk)
2006-11-28 02:13:01 +03:00
action = None
if action == 'y':
transplants.append(node)
elif action == 'm':
merges.append(node)
elif action == 'c':
break
elif action == 'q':
transplants = ()
merges = ()
break
displayer.close()
2006-11-28 02:13:01 +03:00
return (transplants, merges)
@command('transplant',
2013-04-16 21:20:23 +04:00
[('s', 'source', '', _('transplant changesets from REPO'), _('REPO')),
('b', 'branch', [], _('use this source changeset as head'), _('REV')),
('a', 'all', None, _('pull all changesets up to the --branch revisions')),
('p', 'prune', [], _('skip over REV'), _('REV')),
('m', 'merge', [], _('merge at REV'), _('REV')),
('', 'parent', '',
_('parent to choose when transplanting merge'), _('REV')),
2011-10-11 08:07:09 +04:00
('e', 'edit', False, _('invoke editor on commit messages')),
('', 'log', None, _('append transplant info to log message')),
('c', 'continue', None, _('continue last transplant session '
2013-04-16 21:20:23 +04:00
'after fixing conflicts')),
('', 'filter', '',
_('filter changesets through command'), _('CMD'))],
_('hg transplant [-s REPO] [-b BRANCH [-a]] [-p REV] '
'[-m REV] [REV]...'))
2006-11-28 02:13:01 +03:00
def transplant(ui, repo, *revs, **opts):
'''transplant changesets from another branch
Selected changesets will be applied on top of the current working
directory with the log of the original changeset. The changesets
2013-04-16 21:20:23 +04:00
are copied and will thus appear twice in the history with different
identities.
Consider using the graft command if everything is inside the same
repository - it will use merges and will usually give a better result.
Use the rebase extension if the changesets are unpublished and you want
to move them instead of copying them.
If --log is specified, log messages will have a comment appended
of the form::
2006-11-28 02:13:01 +03:00
2009-07-23 01:26:27 +04:00
(transplanted from CHANGESETHASH)
2006-11-28 02:13:01 +03:00
You can rewrite the changelog message with the --filter option.
Its argument will be invoked with the current changelog message as
$1 and the patch as $2.
2006-11-28 02:13:01 +03:00
2013-04-16 21:20:23 +04:00
--source/-s specifies another repository to use for selecting changesets,
just as if it temporarily had been pulled.
If --branch/-b is specified, these revisions will be used as
2013-10-23 21:49:56 +04:00
heads when deciding which changesets to transplant, just as if only
these revisions had been pulled.
If --all/-a is specified, all the revisions up to the heads specified
with --branch will be transplanted.
2006-11-28 02:13:01 +03:00
Example:
- transplant all changes up to REV on top of your current revision::
hg transplant --branch REV --all
2006-11-28 02:13:01 +03:00
You can optionally mark selected transplanted changesets as merge
changesets. You will not be prompted to transplant any ancestors
of a merged transplant, and you can merge descendants of them
normally instead of transplanting them.
2006-11-28 02:13:01 +03:00
Merge changesets may be transplanted directly by specifying the
proper parent changeset by calling :hg:`transplant --parent`.
If no merges or revisions are provided, :hg:`transplant` will
start an interactive changeset browser.
2006-11-28 02:13:01 +03:00
If a changeset application fails, you can fix the merge by hand
and then resume where you left off by calling :hg:`transplant
--continue/-c`.
2006-11-28 02:13:01 +03:00
'''
def incwalk(repo, csets, match=util.always):
for node in csets:
2006-11-28 02:13:01 +03:00
if match(node):
yield node
def transplantwalk(repo, dest, heads, match=util.always):
'''Yield all nodes that are ancestors of a head but not ancestors
of dest.
If no heads are specified, the heads of repo will be used.'''
if not heads:
heads = repo.heads()
2006-11-28 02:13:01 +03:00
ancestors = []
ctx = repo[dest]
for head in heads:
ancestors.append(ctx.ancestor(repo[head]).node())
for node in repo.changelog.nodesbetween(ancestors, heads)[0]:
2006-11-28 02:13:01 +03:00
if match(node):
yield node
def checkopts(opts, revs):
if opts.get('continue'):
2010-01-25 09:05:27 +03:00
if opts.get('branch') or opts.get('all') or opts.get('merge'):
2009-02-09 02:14:07 +03:00
raise util.Abort(_('--continue is incompatible with '
2013-04-16 21:20:23 +04:00
'--branch, --all and --merge'))
2006-11-28 02:13:01 +03:00
return
if not (opts.get('source') or revs or
opts.get('merge') or opts.get('branch')):
raise util.Abort(_('no source URL, branch revision or revision '
2009-02-09 02:14:07 +03:00
'list provided'))
2006-11-28 02:13:01 +03:00
if opts.get('all'):
if not opts.get('branch'):
raise util.Abort(_('--all requires a branch revision'))
if revs:
2009-02-09 02:14:07 +03:00
raise util.Abort(_('--all is incompatible with a '
'revision list'))
2006-11-28 02:13:01 +03:00
checkopts(opts, revs)
if not opts.get('log'):
opts['log'] = ui.config('transplant', 'log')
if not opts.get('filter'):
opts['filter'] = ui.config('transplant', 'filter')
tp = transplanter(ui, repo, opts)
2006-11-28 02:13:01 +03:00
cmdutil.checkunfinished(repo)
2006-11-28 02:13:01 +03:00
p1, p2 = repo.dirstate.parents()
if len(repo) > 0 and p1 == revlog.nullid:
raise util.Abort(_('no revision checked out'))
2006-11-28 02:13:01 +03:00
if not opts.get('continue'):
if p2 != revlog.nullid:
raise util.Abort(_('outstanding uncommitted merges'))
m, a, r, d = repo.status()[:4]
if m or a or r or d:
raise util.Abort(_('outstanding local changes'))
sourcerepo = opts.get('source')
if sourcerepo:
peer = hg.peer(repo, opts, ui.expandpath(sourcerepo))
heads = map(peer.lookup, opts.get('branch', ()))
source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
onlyheads=heads, force=True)
2006-11-28 02:13:01 +03:00
else:
source = repo
heads = map(source.lookup, opts.get('branch', ()))
cleanupfn = None
2006-11-28 02:13:01 +03:00
try:
if opts.get('continue'):
tp.resume(repo, source, opts)
2006-11-28 02:13:01 +03:00
return
2010-02-08 17:36:34 +03:00
tf = tp.transplantfilter(repo, source, p1)
2006-11-28 02:13:01 +03:00
if opts.get('prune'):
2013-04-16 21:31:59 +04:00
prune = set(source.lookup(r)
for r in scmutil.revrange(source, opts.get('prune')))
2006-11-28 02:13:01 +03:00
matchfn = lambda x: tf(x) and x not in prune
else:
matchfn = tf
merges = map(source.lookup, opts.get('merge', ()))
revmap = {}
if revs:
for r in scmutil.revrange(source, revs):
2006-11-28 02:13:01 +03:00
revmap[int(r)] = source.lookup(r)
elif opts.get('all') or not merges:
if source != repo:
alltransplants = incwalk(source, csets, match=matchfn)
2006-11-28 02:13:01 +03:00
else:
alltransplants = transplantwalk(source, p1, heads,
2009-02-09 02:14:07 +03:00
match=matchfn)
2006-11-28 02:13:01 +03:00
if opts.get('all'):
revs = alltransplants
else:
revs, newmerges = browserevs(ui, source, alltransplants, opts)
merges.extend(newmerges)
for r in revs:
revmap[source.changelog.rev(r)] = r
for r in merges:
revmap[source.changelog.rev(r)] = r
tp.apply(repo, source, revmap, merges, opts)
finally:
if cleanupfn:
cleanupfn()
2006-11-28 02:13:01 +03:00
def revsettransplanted(repo, subset, x):
2011-05-06 16:37:38 +04:00
"""``transplanted([set])``
Transplanted changesets in set, or all transplanted changesets.
"""
if x:
s = revset.getset(repo, subset, x)
else:
s = subset
return revset.baseset([r for r in s if
repo[r].extra().get('transplant_source')])
def kwtransplanted(repo, ctx, **args):
""":transplanted: String. The node identifier of the transplanted
changeset if any."""
n = ctx.extra().get('transplant_source')
return n and revlog.hex(n) or ''
def extsetup(ui):
revset.symbols['transplanted'] = revsettransplanted
templatekw.keywords['transplanted'] = kwtransplanted
cmdutil.unfinishedstates.append(
['series', True, False, _('transplant in progress'),
_("use 'hg transplant --continue' or 'hg update' to abort")])
# tell hggettext to extract docstrings from these functions:
i18nfunctions = [revsettransplanted, kwtransplanted]