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