#!/usr/bin/env python """ Helper used by git-multidiff. This script is used as the "difftool" when invoking git-difftool. It creates copies of (or hardlinks to) files that appear to be temporary, and records the names of the files that git-multidiff will need to diff. """ import errno import os import shutil import stat import sys __copyright__ = "Copyright 2012 Laurence Gonsalves" __author__ = "Laurence Gonsalves" __license__ = "GPLv2" __email__ = "laurence@xenomachina.com" DEBUG = False # I can't believe this isn't already in shutil. def mkdirp(dir): """ Python version of 'mkdir -p'. Creates directory, including parent directories. Doesn't get upset if directory already exists. """ try: os.makedirs(dir) except OSError as e: if e.errno != errno.EEXIST: raise def looks_like_a_temp_file(fnam): """ Returns True iff the file appears to be a git-difftool temporary file. """ return ( # temp files are all absolute os.path.isabs(fnam) # /dev/null is probably the only non-regular file we'll ever see, but # lets assume all special files are permanent. and stat.S_ISREG(os.stat(fnam).st_mode)) def main(argv): # TODO: find some way to pass fatal errors up to top-level? # TODO: use "git rev-parse --show-toplevel" and friends when # GIT_MULTIDIFF_TEMP is not set. tmpdir = os.environ['GIT_MULTIDIFF_TEMP'] out = open(os.path.join(tmpdir, 'args'), 'a') for fnam in argv[1:]: if looks_like_a_temp_file(fnam): # A leading double-slash has system defined meaning in POSIX so # treat such files as different from others just in case. root = 'dslash' if fnam.startswith('//') else 'slash' # The '' here adds the path separator at the end. dest = os.path.join(tmpdir, root, '') # XXX Use + instead of os.join because os.join would ignore the # left arg if the right arg is absolute. Unfortunately, doing this # may not be entirely portable, especially if the file system # doesn't like seeing multiple path separators in a row. dest += fnam mkdirp(os.path.dirname(dest)) try: os.link(fnam, dest) if DEBUG: print 'ln %r -> %r' % (fnam, dest) except OSError: shutil.copy2(fnam, dest) if DEBUG: print 'cp %r -> %r' % (fnam, dest) else: dest = fnam if not os.path.isabs(dest): # git-multidiff invokes the diff from the directory in which # git-multidiff was invoked, but _git-multidiff-helper is invoked # from the repo's root. Because these may not be the same, we # need to adjust relative filenames to be absolute, or better yet, # to be relative to the directory in which the diff will run. dest = os.path.abspath(dest) git_prefix = os.environ.get('GIT_PREFIX') if git_prefix is not None: dest = os.path.relpath(dest, os.environ['GIT_PREFIX']) # TODO: else use "git rev-parse --show-toplevel" and friends out.write(dest) out.write('\0') out.close() if __name__ == '__main__': main(sys.argv)