#!/usr/bin/env python3
import argparse
import os
import subprocess

def find_git_dir():
    res = subprocess.run(["git", "rev-parse", "--git-common-dir"],
                         stdout=subprocess.PIPE,
                         check=True)
    return res.stdout.decode().strip()

def probe_object_dir(path):
    for p in [f"{path}/.git/objects", f"{path}/objects", path]:
        if os.path.exists(p):
            return p
    raise FileNotFoundError(path)

def git_repack_thick():
    # "-a" Repack existing packs into one
    # "-d" Prune redundant packs
    res = subprocess.run(["git", "repack", "-a", "-d"], check=True)

def git_repack_thin():
    # "-a" Repack existing packs into one
    # "-d" Prune redundant packs
    # "-l" Don't pack borrowed objects
    res = subprocess.run(["git", "repack", "-a", "-d", "-l"], check=True)

def load_alternates(file):
    try:
        with open(file, "r") as fh:
            return [l.strip() for l in fh]
    except FileNotFoundError:
        return []

def save_alternates(file, lines):
    if lines:
        with open(file, "w") as fh:
            print(*lines, sep="\n", file=fh)
    else:
        try:
            os.unlink(file)
        except FileNotFoundError:
            pass

parser = argparse.ArgumentParser()
parser.add_argument("-a", "--add", metavar="PATH", action="append", default=[])
parser.add_argument("-d", "--remove", metavar="PATH", action="append", default=[])
parser.add_argument("-D", "--clear", action="store_true")
parser.add_argument("-R", "--repack", action="store_true")
args = parser.parse_args()

git_dir = find_git_dir()

alt_file = f"{git_dir}/objects/info/alternates"

if len(args.add) + len(args.remove) + args.clear > 1:
    exit("error: More than one action (add/remove) specified")

if args.repack and (args.remove or args.clear):
    print("Repacking repository as thick.")
    git_repack_thick()

if args.clear:
    print("Clearing alternates list.")
    save_alternates(alt_file, None)

if args.remove:
    alt_paths = load_alternates(alt_file)
    for path in args.remove:
        if path not in alt_paths:
            path = probe_object_dir(path)
        if path not in alt_paths:
            print(f"notice: Path {path!r} not present in alternates.")
            continue
        print(f"Removing {path!r} from alternates list.")
        alt_paths.append(path)
    save_alternates(alt_file, alt_paths)

if args.add:
    alt_paths = load_alternates(alt_file)
    for path in args.add:
        path = probe_object_dir(path)
        if path in alt_paths:
            print(f"notice: Path {path!r} already present in alternates.")
            continue
        print(f"Adding {path!r} to alternates list.")
        alt_paths.append(path)
    save_alternates(alt_file, alt_paths)

if args.repack and (args.add):
    print("Repacking repository as thin.")
    git_repack_thin()

if not (args.add or args.remove or args.clear):
    print("Current alternate paths:")
    alt_paths = load_alternates(alt_file)
    for path in alt_paths:
        print(f"\t{path}")
    if not alt_paths:
        print(f"\t(none)")
