#!/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)