summaryrefslogtreecommitdiff
path: root/hgext/transplant.py
diff options
context:
space:
mode:
Diffstat (limited to 'hgext/transplant.py')
-rw-r--r--hgext/transplant.py125
1 files changed, 41 insertions, 84 deletions
diff --git a/hgext/transplant.py b/hgext/transplant.py
index a506c0c..90f99eb 100644
--- a/hgext/transplant.py
+++ b/hgext/transplant.py
@@ -20,12 +20,8 @@ from mercurial import bundlerepo, hg, merge, match
from mercurial import patch, revlog, scmutil, util, error, cmdutil
from mercurial import revset, templatekw
-class TransplantError(error.Abort):
- pass
-
cmdtable = {}
command = cmdutil.command(cmdtable)
-testedwith = 'internal'
class transplantentry(object):
def __init__(self, lnode, rnode):
@@ -85,25 +81,19 @@ class transplanter(object):
self.opener = scmutil.opener(self.path)
self.transplants = transplants(self.path, 'transplants',
opener=self.opener)
- self.editor = None
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)
+ or has already been transplanted'''
if hasnode(repo, node):
- rev = repo.changelog.rev(node)
- reachable = repo.changelog.incancestors([parentrev], rev)
- if rev in reachable:
+ if node in repo.changelog.reachable(parent, stop=node):
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.incancestors([parentrev], lnoderev):
+ if t.lnode in repo.changelog.reachable(parent, stop=t.lnode):
return True
return False
@@ -115,11 +105,10 @@ class transplanter(object):
diffopts = patch.diffopts(self.ui, opts)
diffopts.git = True
- lock = wlock = tr = None
+ lock = wlock = None
try:
wlock = repo.wlock()
lock = repo.lock()
- tr = repo.transaction('transplant')
for rev in revs:
node = revmap[rev]
revstr = '%s:%s' % (rev, short(node))
@@ -130,7 +119,7 @@ class transplanter(object):
continue
parents = source.changelog.parents(node)
- if not (opts.get('filter') or opts.get('log')):
+ if not opts.get('filter'):
# If the changeset parent is the same as the
# wdir's parent, just pull it.
if parents[0] == p1:
@@ -139,7 +128,7 @@ class transplanter(object):
continue
if pulls:
if source != repo:
- repo.pull(source.peer(), heads=pulls)
+ repo.pull(source, heads=pulls)
merge.update(repo, pulls[-1], False, False, None)
p1, p2 = repo.dirstate.parents()
pulls = []
@@ -153,26 +142,14 @@ class transplanter(object):
if not hasnode(repo, node):
repo.pull(source, heads=[node])
- skipmerge = False
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:
+ self.ui.note(_('skipping merge changeset %s:%s\n')
+ % (rev, short(node)))
patchfile = None
else:
fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
fp = os.fdopen(fd, 'w')
- gen = patch.diff(source, parent, node, opts=diffopts)
+ gen = patch.diff(source, parents[0], node, opts=diffopts)
for chunk in gen:
fp.write(chunk)
fp.close()
@@ -180,17 +157,11 @@ class transplanter(object):
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
+ n = self.applyone(repo, node,
+ source.changelog.read(node),
+ patchfile, merge=domerge,
+ log=opts.get('log'),
+ filter=opts.get('filter'))
if n and domerge:
self.ui.status(_('%s merged at %s\n') % (revstr,
short(n)))
@@ -201,15 +172,12 @@ class transplanter(object):
finally:
if patchfile:
os.unlink(patchfile)
- tr.close()
if pulls:
- repo.pull(source.peer(), heads=pulls)
+ repo.pull(source, heads=pulls)
merge.update(repo, pulls[-1], False, False, None)
finally:
self.saveseries(revmap, merges)
self.transplants.write()
- if tr:
- tr.release()
lock.release()
wlock.release()
@@ -263,6 +231,9 @@ class transplanter(object):
files = set()
patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
files = list(files)
+ if not files:
+ self.ui.warn(_('%s: empty changeset') % revlog.hex(node))
+ return None
except Exception, inst:
seriespath = os.path.join(self.path, 'series')
if os.path.exists(seriespath):
@@ -271,22 +242,27 @@ class transplanter(object):
p2 = node
self.log(user, date, message, p1, p2, merge=merge)
self.ui.write(str(inst) + '\n')
- raise TransplantError(_('fix up the merge and run '
- 'hg transplant --continue'))
+ raise util.Abort(_('fix up the merge and run '
+ 'hg transplant --continue'))
else:
files = None
if merge:
p1, p2 = repo.dirstate.parents()
- repo.setparents(p1, node)
+ repo.dirstate.setparents(p1, node)
m = match.always(repo.root, '')
else:
m = match.exact(repo.root, '', files)
- n = repo.commit(message, user, date, extra=extra, match=m,
- editor=self.editor)
+ n = repo.commit(message, user, date, extra=extra, match=m)
if not n:
- self.ui.warn(_('skipping emptied changeset %s\n') % short(node))
- return None
+ # Crash here to prevent an unclear crash later, in
+ # transplants.write(). This can happen if patch.patch()
+ # does nothing but claims success or if repo.status() fails
+ # to report changes done by patch.patch(). These both
+ # appear to be bugs in other parts of Mercurial, but dying
+ # here, as soon as we can detect the problem, is preferable
+ # to silently dropping changesets on the floor.
+ raise RuntimeError('nothing committed after transplant')
if not merge:
self.transplants.set(n, node)
@@ -313,33 +289,22 @@ class transplanter(object):
def recover(self, repo):
'''commit working directory using journal metadata'''
node, user, date, message, parents = self.readlog()
- merge = False
+ merge = len(parents) == 2
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}
wlock = repo.wlock()
try:
p1, p2 = repo.dirstate.parents()
- if p1 != parent:
+ if p1 != parents[0]:
raise util.Abort(
_('working dir not at transplant parent %s') %
- revlog.hex(parent))
+ revlog.hex(parents[0]))
if merge:
- repo.setparents(p1, parents[1])
- n = repo.commit(message, user, date, extra=extra,
- editor=self.editor)
+ repo.dirstate.setparents(p1, parents[1])
+ n = repo.commit(message, user, date, extra=extra)
if not n:
raise util.Abort(_('commit failed'))
if not merge:
@@ -496,9 +461,6 @@ def browserevs(ui, repo, nodes, opts):
('a', 'all', None, _('pull all changesets up to BRANCH')),
('p', 'prune', [], _('skip over REV'), _('REV')),
('m', 'merge', [], _('merge at REV'), _('REV')),
- ('', 'parent', '',
- _('parent to choose when transplanting merge'), _('REV')),
- ('e', 'edit', False, _('invoke editor on commit messages')),
('', 'log', None, _('append transplant info to log message')),
('c', 'continue', None, _('continue last transplant session '
'after repair')),
@@ -531,7 +493,7 @@ def transplant(ui, repo, *revs, **opts):
transplanted, otherwise you will be prompted to select the
changesets you want.
- :hg:`transplant --branch REV --all` will transplant the
+ :hg:`transplant --branch REVISION --all` will transplant the
selected branch (up to the named revision) onto your current
working directory.
@@ -540,9 +502,6 @@ def transplant(ui, repo, *revs, **opts):
of a merged transplant, and you can merge descendants of them
normally instead of transplanting them.
- 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.
@@ -590,8 +549,6 @@ def transplant(ui, repo, *revs, **opts):
opts['filter'] = ui.config('transplant', 'filter')
tp = transplanter(ui, repo)
- if opts.get('edit'):
- tp.editor = cmdutil.commitforceeditor
p1, p2 = repo.dirstate.parents()
if len(repo) > 0 and p1 == revlog.nullid:
@@ -605,9 +562,9 @@ def transplant(ui, repo, *revs, **opts):
sourcerepo = opts.get('source')
if sourcerepo:
- peer = hg.peer(ui, opts, ui.expandpath(sourcerepo))
- branches = map(peer.lookup, opts.get('branch', ()))
- source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer,
+ source = hg.peer(ui, opts, ui.expandpath(sourcerepo))
+ branches = map(source.lookup, opts.get('branch', ()))
+ source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source,
onlyheads=branches, force=True)
else:
source = repo
@@ -657,9 +614,9 @@ def revsettransplanted(repo, subset, x):
Transplanted changesets in set, or all transplanted changesets.
"""
if x:
- s = revset.getset(repo, subset, x)
+ s = revset.getset(repo, subset, x)
else:
- s = subset
+ s = subset
return [r for r in s if repo[r].extra().get('transplant_source')]
def kwtransplanted(repo, ctx, **args):