#!/usr/bin/env python """ Like git-difftool, but executes all diffs in parallel. Installation: - Put this and _git-multidiff-helper in your path. - Set the git config variable multidiff.tool to a multidiff tool. This is any program that when given pairs of filenames on the commandline will show you the pair-wise diffs. Make sure that this command does not background itself, as numerous temporary files will be obliterated the moment it returns. If you like vim(diff), you might want to try my 'vd' wrapper script. Here's an example .gitconfig snippet for using it: [multidiff] tool = vd -f """ import commands import os import shutil import stat import subprocess import sys import tempfile __copyright__ = "Copyright 2012 Laurence Gonsalves" __author__ = "Laurence Gonsalves" __license__ = "GPLv2" __email__ = "laurence@xenomachina.com" DEBUG = False def main(argv): try: tool = subprocess.check_output(['git', 'config', '--get', 'multidiff.tool']).strip() except subprocess.CalledProcessError as exc: print >>sys.stderr, \ 'Error: %r returned status %r' % (' '.join(exc.cmd), exc.returncode) sys.exit(1) tmpdir = os.path.join(tempfile.mkdtemp(), '') try: # TODO: use "git rev-parse --show-toplevel" and friends when # GIT_MULTIDIFF_TEMP is not set. os.environ['GIT_MULTIDIFF_TEMP'] = tmpdir argsfile = os.path.join(tmpdir, 'args') open(argsfile, 'w').close() subprocess.check_call(['git', 'difftool', '-y', '-x', '_git-multidiff-helper'] + argv[1:]) difftool_args = [x for x in open(argsfile).read().split('\0') if x] assert not (len(difftool_args) % 2), \ "Expected even number of files, but got %d" % len(difftool_args) for fnam in set(difftool_args): if fnam.startswith(tmpdir): file_stat = os.stat(fnam) if file_stat.st_nlink == 1: # Try to make copy read-only. Making the temporary files # read-only is nice for diff tools that allow editing (like # vimdiff) as this provides a hint that the temporary file # should not be edited. # # We only do this if nlink==1 on the off chance that git is # still using the file we hard-linked (we don't want to alter # shared permissions). try: perms = file_stat.st_mode \ & ~(stat.S_IWGRP | stat.S_IWOTH | stat.S_IWUSR) os.chmod(fnam, perms) if DEBUG: print 'chmod a-r %r' % fnam except IOError: pass # we tried, but it isn't critical that we succeed # TODO: attempt to shorten pathnames of temp files? if difftool_args: # Instead of using `subprocess.check_call([tool] + difftool_args)` # we use os.system. This makes it possible for the tool to contain # flags (or potentially other stuff evaluated by the shell). os.system(tool + ''.join(commands.mkarg(x) for x in difftool_args)) finally: shutil.rmtree(tmpdir, ignore_errors=True) if __name__ == '__main__': main(sys.argv)