[PATCH] filemerge: support passing python script to custom merge-tools
Tom Hindle
tom_hindle at sil.org
Wed Apr 18 09:32:52 EDT 2018
# HG changeset patch
# User hindlemail <tom_hindle at sil.org>
# Date 1523988043 21600
# Tue Apr 17 12:00:43 2018 -0600
# Node ID 701f7b1145cb8105428141c22260c89d6b942bef
# Parent 62ebfda864de35f306963989303b70235aa63952
filemerge: support passing python script to custom merge-tools
Eliminates the need to specify a python execuatable, which may not exist on
system. Additionally launching script inprocess aids portablity on systems
that
can't execute python via the shell.
example usage "merge-tools.myTool.executable=python:c:\myTool.py"
diff -r 62ebfda864de -r 701f7b1145cb mercurial/filemerge.py
--- a/mercurial/filemerge.py Fri Apr 13 11:54:13 2018 -0700
+++ b/mercurial/filemerge.py Tue Apr 17 12:00:43 2018 -0600
@@ -6,16 +6,17 @@
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import contextlib
import os
import re
import shutil
+import sys
import tempfile
from .i18n import _
from .node import nullid, short
from . import (
encoding,
error,
@@ -109,16 +110,19 @@ class absentfilectx(object):
return False
def isabsent(self):
return True
def _findtool(ui, tool):
if tool in internals:
return tool
+ cmd = _toolstr(ui, tool, "executable", tool)
+ if cmd.startswith('python:'):
+ return cmd
return findexternaltool(ui, tool)
def findexternaltool(ui, tool):
for kn in ("regkey", "regkeyalt"):
k = _toolstr(ui, tool, kn)
if not k:
continue
p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
@@ -320,17 +324,17 @@ def _underlyingfctxifabsent(filectx):
underyling workingfilectx in that case.
"""
if filectx.isabsent():
return filectx.changectx()[filectx.path()]
else:
return filectx
def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, script = toolconf
if symlink or fcd.isabsent() or fco.isabsent():
return 1
unused, unused, unused, back = files
ui = repo.ui
validkeep = ['keep', 'keep-merge3']
@@ -356,17 +360,17 @@ def _premerge(repo, fcd, fco, fca, toolc
ui.debug(" premerge successful\n")
return 0
if premerge not in validkeep:
# restore from backup and try again
_restorebackup(fcd, back)
return 1 # continue merging
def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, script = toolconf
if symlink:
repo.ui.warn(_('warning: internal %s cannot merge symlinks '
'for %s\n') % (tool, fcd.path()))
return False
if fcd.isabsent() or fco.isabsent():
repo.ui.warn(_('warning: internal %s cannot merge change/delete '
'conflict for %s\n') % (tool, fcd.path()))
return False
@@ -425,17 +429,17 @@ def _imerge3(repo, mynode, orig, fcd, fc
return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files,
labels)
def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
labels=None, localorother=None):
"""
Generic driver for _imergelocal and _imergeother
"""
assert localorother is not None
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, script = toolconf
r = simplemerge.simplemerge(repo.ui, fcd, fca, fco, label=labels,
localorother=localorother)
return True, r
@internaltool('merge-local', mergeonly, precheck=_mergecheck)
def _imergelocal(*args, **kwargs):
"""
Like :merge, but resolve all conflicts non-interactively in favor
@@ -505,17 +509,17 @@ def _xmergeimm(repo, mynode, orig, fcd,
# raises the question of what to do if the user only partially
resolves the
# file -- we can't leave a merge state. (Copy to somewhere in the .hg/
# directory and tell the user how to get it is my best idea, but it's
# clunky.)
raise error.InMemoryMergeConflictsError('in-memory merge does not
support '
'external merge tools')
def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files,
labels=None):
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, script = toolconf
if fcd.isabsent() or fco.isabsent():
repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
'for %s\n') % (tool, fcd.path()))
return False, 1, None
unused, unused, unused, back = files
localpath = _workingpath(repo, fcd)
args = _toolstr(repo.ui, tool, "args")
@@ -551,17 +555,27 @@ def _xmerge(repo, mynode, orig, fcd, fco
args = util.interpolate(
br'\$', replace, args,
lambda s: procutil.shellquote(util.localpath(s)))
cmd = toolpath + ' ' + args
if _toolbool(ui, tool, "gui"):
repo.ui.status(_('running merge tool %s for file %s\n') %
(tool, fcd.path()))
repo.ui.debug('launching merge tool: %s\n' % cmd)
- r = ui.system(cmd, cwd=repo.root, environ=env,
blockedtag='mergetool')
+ if not script:
+ r = ui.system(cmd, cwd=repo.root, environ=env,
+ blockedtag='mergetool')
+ else:
+ r = 0
+ try:
+ sys.argv = procutil.shellsplit(cmd)
+ execfile(procutil.shellsplit(toolpath)[0], {})
+ except Exception as err:
+ r = 1
+ repo.ui.debug('merge tool threw exception: %s\n' %
err.args[0])
repo.ui.debug('merge tool returned: %d\n' % r)
return True, r, False
def _formatconflictmarker(ctx, template, label, pad):
"""Applies the given template to the ctx, prefixed by the label.
Pad is the minimum width of the label prefix, so that multiple markers
can have aligned templated parts.
@@ -746,19 +760,23 @@ def _filemerge(premerge, repo, wctx, myn
return True, None, False
ui = repo.ui
fd = fcd.path()
binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
symlink = 'l' in fcd.flags() + fco.flags()
changedelete = fcd.isabsent() or fco.isabsent()
tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
+ script = False
if tool in internals and tool.startswith('internal:'):
# normalize to new-style names (':merge' etc)
tool = tool[len('internal'):]
+ if toolpath and procutil.shellsplit(toolpath)[0].startswith('python:'):
+ toolpath = toolpath.replace('python:', '')
+ script = True
ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete
%s)\n"
% (tool, fd, pycompat.bytestr(binary),
pycompat.bytestr(symlink),
pycompat.bytestr(changedelete)))
if tool in internals:
func = internals[tool]
mergetype = func.mergetype
onfailure = func.onfailure
@@ -769,17 +787,17 @@ def _filemerge(premerge, repo, wctx, myn
func = _xmergeimm
else:
func = _xmerge
mergetype = fullmerge
onfailure = _("merging %s failed!\n")
precheck = None
isexternal = True
- toolconf = tool, toolpath, binary, symlink
+ toolconf = tool, toolpath, binary, symlink, script
if mergetype == nomerge:
r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf,
labels)
return True, r, deleted
if premerge:
if orig != fco.path():
ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(),
fd))
On Tue, Apr 17, 2018 at 4:28 PM, Tom Hindle <tom_hindle at sil.org> wrote:
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.mercurial-scm.org/pipermail/mercurial-devel/attachments/20180418/a4463d00/attachment.html>
More information about the Mercurial-devel
mailing list