# HG changeset patch # User hindlemail <tom_hin...@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) _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel