[PATCH] filemerge: support specfiying a python function to custom merge-tools
tom_hindle at sil.org
tom_hindle at sil.org
Wed May 9 18:33:49 UTC 2018
# HG changeset patch
# User hindlemail <tom_hindle at sil.org>
# Date 1525890682 21600
# Wed May 09 12:31:22 2018 -0600
# Node ID ac1af9a3417eddaee93635682d3ebcdb8a929a8a
# Parent 8b86acc7aa64130f5b6fa69f5fc20ef4d0b09c42
filemerge: support specfiying a python function to custom merge-tools
Eliminates the need to specify a python executable, 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:mergefn"
where myTool.py contains a function:
"def mergefn(ui, repo, args, **kwargs):"
where args is list of args passed to merge tool.
(by default, expanded: $local $base $other)
invoking the specified python function was done by exposing and invoking
(hook._pythonhook -> hook.pythonhook)
extensions and hooks were imported locally because of cycles:
cmdutil -> merge -> filemerge -> extensions -> cmdutil
cmdutil -> merge -> filemerge -> hook -> extensions -> cmdutil
diff -r 8b86acc7aa64 -r ac1af9a3417e mercurial/filemerge.py
--- a/mercurial/filemerge.py Sat Apr 28 23:16:41 2018 -0700
+++ b/mercurial/filemerge.py Wed May 09 12:31:22 2018 -0600
@@ -114,6 +114,9 @@
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):
@@ -325,7 +328,7 @@
return filectx
def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, scriptfn = toolconf
if symlink or fcd.isabsent() or fco.isabsent():
return 1
unused, unused, unused, back = files
@@ -361,7 +364,7 @@
return 1 # continue merging
def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, scriptfn = toolconf
if symlink:
repo.ui.warn(_('warning: internal %s cannot merge symlinks '
'for %s\n') % (tool, fcd.path()))
@@ -430,7 +433,7 @@
Generic driver for _imergelocal and _imergeother
"""
assert localorother is not None
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, scriptfn = toolconf
r = simplemerge.simplemerge(repo.ui, fcd, fca, fco, label=labels,
localorother=localorother)
return True, r
@@ -510,7 +513,7 @@
'external merge tools')
def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
- tool, toolpath, binary, symlink = toolconf
+ tool, toolpath, binary, symlink, scriptfn = toolconf
if fcd.isabsent() or fco.isabsent():
repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
'for %s\n') % (tool, fcd.path()))
@@ -551,12 +554,35 @@
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 scriptfn is None:
+ cmd = toolpath + ' ' + args
+ repo.ui.debug('launching merge tool: %s\n' % cmd)
+ r = ui.system(cmd, cwd=repo.root, environ=env,
+ blockedtag='mergetool')
+ else:
+ repo.ui.debug('launching python merge script: %s:%s\n' %
+ (toolpath, scriptfn))
+ r = 0
+ try:
+ from . import extensions
+ mod = extensions.loadpath(toolpath, 'hgmerge.%s' % scriptfn)
+ except Exception:
+ ui.write(_("loading %s python merge script failed\n") %
+ toolpath)
+ raise
+ mergefn = getattr(mod, scriptfn, None)
+ if mergefn is None:
+ raise error.Abort(_("%s does not have function: %s\n") %
+ (toolpath, scriptfn))
+ argslist = procutil.shellsplit(args)
+ from . import hook
+ ret, raised = hook.pythonhook(ui, repo, "merge", toolpath,
+ mergefn, {'args' : argslist}, True)
+ if raised:
+ r = 1
repo.ui.debug('merge tool returned: %d\n' % r)
return True, r, False
@@ -751,9 +777,25 @@
symlink = 'l' in fcd.flags() + fco.flags()
changedelete = fcd.isabsent() or fco.isabsent()
tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
+ scriptfn = None
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:'):
+ invalidsyntax = False
+ if toolpath.count(':') >= 2:
+ script, scriptfn = procutil.shellsplit(toolpath)[0][7:].rsplit(':',
+ 1)
+ if not scriptfn:
+ invalidsyntax = True
+ # missing :callable can lead to spliting on windows drive letter
+ if '\\' in scriptfn or '/' in scriptfn:
+ invalidsyntax = True
+ else:
+ invalidsyntax = True
+ if invalidsyntax:
+ raise error.Abort(_("invalid 'python:' syntax: %s \n") % toolpath)
+ toolpath = script
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)))
@@ -774,7 +816,7 @@
precheck = None
isexternal = True
- toolconf = tool, toolpath, binary, symlink
+ toolconf = tool, toolpath, binary, symlink, scriptfn
if mergetype == nomerge:
r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
diff -r 8b86acc7aa64 -r ac1af9a3417e mercurial/hook.py
--- a/mercurial/hook.py Sat Apr 28 23:16:41 2018 -0700
+++ b/mercurial/hook.py Wed May 09 12:31:22 2018 -0600
@@ -24,7 +24,7 @@
stringutil,
)
-def _pythonhook(ui, repo, htype, hname, funcname, args, throw):
+def pythonhook(ui, repo, htype, hname, funcname, args, throw):
'''call python hook. hook is callable object, looked up as
name in python module. if callable returns "true", hook
fails, else passes. if hook raises exception, treated as
@@ -242,7 +242,7 @@
r = 1
raised = False
elif callable(cmd):
- r, raised = _pythonhook(ui, repo, htype, hname, cmd, args,
+ r, raised = pythonhook(ui, repo, htype, hname, cmd, args,
throw)
elif cmd.startswith('python:'):
if cmd.count(':') >= 2:
@@ -258,7 +258,7 @@
hookfn = getattr(mod, cmd)
else:
hookfn = cmd[7:].strip()
- r, raised = _pythonhook(ui, repo, htype, hname, hookfn, args,
+ r, raised = pythonhook(ui, repo, htype, hname, hookfn, args,
throw)
else:
r = _exthook(ui, repo, htype, hname, cmd, args, throw)
More information about the Mercurial-devel
mailing list