#!/usr/bin/env python3 import argparse from os import path, makedirs, walk, listdir, remove, stat, chmod from shutil import copy, rmtree from subprocess import run from shlex import quote from folder import folder def make_executable(path): # thanks to Jonathon Reinhart on SO mode = stat(path).st_mode mode |= (mode & 0o444) >> 2 # copy R bits to X chmod(path, mode) def relpath(dest, src): return path.relpath(path.abspath(dest), path.abspath(src)) def path_to_script(script_name, from_worktree): script_path = path.join(path.dirname(__file__), script_name) return relpath(script_path, from_worktree) parser = argparse.ArgumentParser(description=''' Expand a patchset into a git repository (based on git-am) ''') parser.add_argument('patchset', metavar='PATCHSET', action='store', type=lambda x: folder('patchset', x), help='Patchset (assumed in ../patchset/)') parser.add_argument('worktree', metavar='WORKTREE', action='store', type=lambda x: folder('worktree', x), help='Destination worktree (assumed in ../worktree/)') group = parser.add_mutually_exclusive_group() group.add_argument('--base', metavar='SRC', action='store', default='SuperMarioProj.1994-02-09', type=lambda x: folder('base', x), help='Base source tree (default=SuperMarioProj.1994-02-09, assumed in ../base/)') args = parser.parse_args() assert path.exists(args.patchset) assert not path.exists(path.join(args.patchset, '.git')) # protect against argument swap horst = ['-c', 'user.name=Horst Beepmanh', '-c', 'user.email=<>'] def git(*gitargs): run(['git', *horst, *gitargs], cwd=args.worktree, check=True) try: rmtree(args.worktree) except FileNotFoundError: pass makedirs(args.worktree) with open(path.join(args.worktree, '.gitignore'), 'a') as f: f.write('.*\n*.dmg\nBuild*/\n') with open(path.join(args.worktree, '.build'), 'w') as f: print('#!/bin/sh', file=f) print('exec ' + quote(path_to_script('build', args.worktree)) + ' . "$@"', file=f) make_executable(f.name) with open(path.join(args.worktree, '.flatten'), 'w') as f: print('#!/bin/sh', file=f) print('exec ' + quote(path_to_script('flatten', args.worktree)) + ' . ' + relpath(args.patchset, args.worktree) + ' "$@"', file=f) make_executable(f.name) git('init') git('add', '-f', '.gitignore', '.build', '.flatten') git('commit', '-m', 'Useful non-source things') for walk_base, walk_dirs, walk_files in walk(args.base): walk_dirs[:] = [x for x in walk_dirs if not x.startswith('.')] walk_files[:] = [x for x in walk_files if not x.startswith('.')] other_dir = path.join(args.worktree, path.relpath(walk_base, args.base)) makedirs(other_dir, exist_ok=True) for this_file in walk_files: copy(path.join(walk_base, this_file), other_dir) git('add', '.') git('commit', '-m', path.basename(args.base)) git('tag', 'patchset-base') patchfiles = [path.join(args.patchset, x) for x in sorted(listdir(args.patchset)) if path.splitext(x)[1].lower() == '.patch'] git('am', *patchfiles)