summaryrefslogtreecommitdiff
path: root/bzrlib/mergetools.py
diff options
context:
space:
mode:
authorLorry <lorry@roadtrain.codethink.co.uk>2012-08-22 15:47:16 +0100
committerLorry <lorry@roadtrain.codethink.co.uk>2012-08-22 15:47:16 +0100
commit25335618bf8755ce6b116ee14f47f5a1f2c821e9 (patch)
treed889d7ab3f9f985d0c54c534cb8052bd2e6d7163 /bzrlib/mergetools.py
downloadbzr-tarball-25335618bf8755ce6b116ee14f47f5a1f2c821e9.tar.gz
Tarball conversion
Diffstat (limited to 'bzrlib/mergetools.py')
-rw-r--r--bzrlib/mergetools.py127
1 files changed, 127 insertions, 0 deletions
diff --git a/bzrlib/mergetools.py b/bzrlib/mergetools.py
new file mode 100644
index 0000000..db8255b
--- /dev/null
+++ b/bzrlib/mergetools.py
@@ -0,0 +1,127 @@
+# Copyright (C) 2010 Canonical Ltd.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Utility functions for managing external merge tools such as kdiff3."""
+
+from __future__ import absolute_import
+
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+from bzrlib.lazy_import import lazy_import
+lazy_import(globals(), """
+from bzrlib import (
+ cmdline,
+ osutils,
+ trace,
+)
+""")
+
+
+known_merge_tools = {
+ 'bcompare': 'bcompare {this} {other} {base} {result}',
+ 'kdiff3': 'kdiff3 {base} {this} {other} -o {result}',
+ 'xdiff': 'xxdiff -m -O -M {result} {this} {base} {other}',
+ 'meld': 'meld {base} {this_temp} {other}',
+ 'opendiff': 'opendiff {this} {other} -ancestor {base} -merge {result}',
+ 'winmergeu': 'winmergeu {result}',
+}
+
+
+def check_availability(command_line):
+ cmd_list = cmdline.split(command_line)
+ exe = cmd_list[0]
+ if sys.platform == 'win32':
+ exe = _get_executable_path(exe)
+ if exe is None:
+ return False
+ base, ext = os.path.splitext(exe)
+ path_ext = [unicode(s.lower())
+ for s in os.getenv('PATHEXT', '').split(os.pathsep)]
+ return os.path.exists(exe) and ext in path_ext
+ else:
+ return (os.access(exe, os.X_OK)
+ or osutils.find_executable_on_path(exe) is not None)
+
+
+def invoke(command_line, filename, invoker=None):
+ """Invokes the given merge tool command line, substituting the given
+ filename according to the embedded substitution markers. Optionally, it
+ will use the given invoker function instead of the default
+ subprocess_invoker.
+ """
+ if invoker is None:
+ invoker = subprocess_invoker
+ cmd_list = cmdline.split(command_line)
+ exe = _get_executable_path(cmd_list[0])
+ if exe is not None:
+ cmd_list[0] = exe
+ args, tmp_file = _subst_filename(cmd_list, filename)
+ def cleanup(retcode):
+ if tmp_file is not None:
+ if retcode == 0: # on success, replace file with temp file
+ shutil.move(tmp_file, filename)
+ else: # otherwise, delete temp file
+ os.remove(tmp_file)
+ return invoker(args[0], args[1:], cleanup)
+
+
+def _get_executable_path(exe):
+ if os.path.isabs(exe):
+ return exe
+ return osutils.find_executable_on_path(exe)
+
+
+def _subst_filename(args, filename):
+ subst_names = {
+ 'base': filename + u'.BASE',
+ 'this': filename + u'.THIS',
+ 'other': filename + u'.OTHER',
+ 'result': filename,
+ }
+ tmp_file = None
+ subst_args = []
+ for arg in args:
+ if '{this_temp}' in arg and not 'this_temp' in subst_names:
+ fh, tmp_file = tempfile.mkstemp(u"_bzr_mergetools_%s.THIS" %
+ os.path.basename(filename))
+ trace.mutter('fh=%r, tmp_file=%r', fh, tmp_file)
+ os.close(fh)
+ shutil.copy(filename + u".THIS", tmp_file)
+ subst_names['this_temp'] = tmp_file
+ arg = _format_arg(arg, subst_names)
+ subst_args.append(arg)
+ return subst_args, tmp_file
+
+
+# This would be better implemented using format() from python 2.6
+def _format_arg(arg, subst_names):
+ arg = arg.replace('{base}', subst_names['base'])
+ arg = arg.replace('{this}', subst_names['this'])
+ arg = arg.replace('{other}', subst_names['other'])
+ arg = arg.replace('{result}', subst_names['result'])
+ if subst_names.has_key('this_temp'):
+ arg = arg.replace('{this_temp}', subst_names['this_temp'])
+ return arg
+
+
+def subprocess_invoker(executable, args, cleanup):
+ retcode = subprocess.call([executable] + args)
+ cleanup(retcode)
+ return retcode