#!/usr/bin/env python3

"""
This is a simple program that will delete files from history which match
current gitignore rules, while also:
  1) pruning commits which become empty
  2) pruning merge commits which become degenerate and have no changes
     relative to its remaining relevant parent
  3) rewriting commit hashes in commit messages to reference new commit IDs.
"""

"""
Please see the
  ***** API BACKWARD COMPATIBILITY CAVEAT *****
near the top of git-filter-repo.
"""

import argparse
import os
import subprocess
import sys
try:
  import git_filter_repo as fr
except ImportError:
  raise SystemExit("Error: Couldn't find git_filter_repo.py.  Did you forget to make a symlink to git-filter-repo named git_filter_repo.py or did you forget to put the latter in your PYTHONPATH?")

class CheckIgnores:
  def __init__(self):
    self.ignored = set()
    self.okay = set()

    cmd = 'git check-ignore --stdin --verbose --non-matching --no-index -z'
    self.check_ignore_process = subprocess.Popen(cmd.split(),
                                                 stdin=subprocess.PIPE,
                                                 stdout=subprocess.PIPE)

  def __del__(self):
    if self.check_ignore_process:
      self.check_ignore_process.stdin.close()

  def get_ignored(self, filenames):
    ignored = set()
    for name in filenames:
      if name in self.ignored:
        ignored.add(name)
      elif name in self.okay:
        continue
      else:
        self.check_ignore_process.stdin.write(name+b'\0')
        self.check_ignore_process.stdin.flush()
        result = os.read(self.check_ignore_process.stdout.fileno(), 65535).rstrip(b'\0')
        (source, linenum, pattern, pathname) = result.split(b"\0")
        if name != pathname:
          raise SystemExit("Error: Passed {} but got {}".format(name, pathname))
        if not source and not linenum and not pattern:
          self.okay.add(name)
        else:
          if pattern[0:1] == b"!":
            self.okay.add(name)
          else:
            self.ignored.add(name)
            ignored.add(name)

    return ignored

  def skip_ignores(self, commit, metadata):
    filenames = [x.filename for x in commit.file_changes]
    bad = self.get_ignored(filenames)
    commit.file_changes = [x for x in commit.file_changes
                           if x.filename not in bad]


def main():
  checker = CheckIgnores()
  args = fr.FilteringOptions.parse_args(sys.argv[1:], error_on_empty = False)
  filter = fr.RepoFilter(args, commit_callback=checker.skip_ignores)
  filter.run()
  

if __name__ == '__main__':
  main()

