diff options
Diffstat (limited to 'hgext/transplant.py')
-rw-r--r-- | hgext/transplant.py | 125 |
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): |