[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