-#!/usr/bin/python
+#!/usr/bin/python3
+
+# pylint: disable=fixme,invalid-name
+# pylint: disable=too-many-branches,too-many-locals,too-many-statements
"""Repack git repos fully the way I like them."""
return ret
+def is_git_dir(path):
+ """Whether |path| is a .git dir"""
+ return (os.path.isdir(os.path.join(path, 'refs')) and
+ os.path.isdir(os.path.join(path, 'objects')) and
+ os.path.isfile(os.path.join(path, 'config')))
+
+
def find_git_dir(path):
"""Try to find the .git dir to operate on"""
orig_path = path
if os.path.isdir(os.path.join(path, '.git')):
curr_path = os.path.join(path, '.git')
- if (os.path.isdir(os.path.join(curr_path, 'refs')) and
- os.path.isdir(os.path.join(curr_path, 'objects')) and
- os.path.isfile(os.path.join(curr_path, 'config'))):
+ if is_git_dir(curr_path):
return curr_path
path = os.path.dirname(path)
def readfile(path):
"""Read |path| and return its data"""
if os.path.isfile(path):
- return open(path).read()
+ with open(path) as fp:
+ return fp.read()
+ return ''
def unlink(path):
def is_packed(path):
"""See if the git repo is already packed"""
- if set(('info', 'pack')) != set(os.listdir(path)):
+ obj_path = os.path.join(path, 'objects')
+ paths = set(os.listdir(obj_path))
+ if paths not in ({'info', 'pack'}, {'pack'}):
return False
- packs = os.listdir(os.path.join(path, 'pack'))
+ packs = os.listdir(os.path.join(obj_path, 'pack'))
if len(packs) != 2:
return False
return True
path = find_git_dir(path)
print('Repacking %s' % path)
+ # Repack any submodules this project might use.
+ modules_path = os.path.join(path, 'modules')
+ if os.path.isdir(modules_path):
+ for root, dirs, _ in os.walk(modules_path):
+ dirs.sort()
+ for d in dirs:
+ mod_path = os.path.join(root, d)
+ if is_git_dir(mod_path):
+ repack(mod_path)
+
tmpdir = find_temp_dir()
if tmpdir:
tmpdir = tempfile.mkdtemp(prefix='git-repack.', dir=tmpdir)
print('Using tempdir: %s' % tmpdir)
os.rmdir(tmpdir)
+ # Doesn't matter for these needs.
+ os.environ['GIT_WORK_TREE'] = tmpdir
grafts = alts = None
try:
clean_hooks(path)
+ # XXX: Should do this for all remotes?
origin_path = os.path.join(path, 'refs', 'remotes', 'origin')
packed_refs = readfile(os.path.join(path, 'packed-refs'))
if os.path.exists(origin_path) or 'refs/remotes/origin/' in packed_refs:
cmd = ['git', '--git-dir', path, 'remote', 'prune', 'origin']
- subprocess.check_call(cmd, cwd='/')
+ subprocess.run(cmd, cwd='/', check=True)
clean_packs(path)
cmd = ['git', '--git-dir', rundir, 'reflog', 'expire', '--all', '--stale-fix']
print('Cleaning reflog: %s' % ' '.join(cmd))
- subprocess.check_call(cmd, cwd='/')
+ subprocess.run(cmd, cwd='/', check=True)
# This also packs refs/tags for us.
cmd = ['git', '--git-dir', rundir, 'gc', '--aggressive', '--prune=all']
print('Repacking git repo: %s' % ' '.join(cmd))
- subprocess.check_call(cmd, cwd='/')
+ subprocess.run(cmd, cwd='/', check=True)
+
+ # Clean empty dirs.
+ cmd = ['find', rundir, '-depth', '-type', 'd', '-exec', 'rmdir', '{}', '+']
+ subprocess.call(cmd, stderr=subprocess.DEVNULL)
+
+ # There's a few dirs we need to exist even if they're empty.
+ refdir = os.path.join(rundir, 'refs')
+ os.makedirs(refdir, exist_ok=True)
if tmpdir:
cmd = ['rsync', '-a', '--delete', tmpdir + '/', path + '/']
print('Syncing back git repo: %s' % ' '.join(cmd))
- subprocess.check_call(cmd, cwd='/')
+ subprocess.run(cmd, cwd='/', check=True)
cmd = ['find', path + '/', '-exec', 'chmod', 'u+rw', '{}', '+']
- subprocess.check_call(cmd, cwd='/')
+ subprocess.run(cmd, cwd='/', check=True)
finally:
if grafts:
- open(graft_file, 'w').write(grafts)
+ with open(graft_file, 'w') as fp:
+ fp.write(grafts)
if alts:
- open(alt_file, 'w').write(alts)
+ with open(alt_file, 'w') as fp:
+ fp.write(alts)
if tmpdir:
- shutil.rmtree(tmpdir)
+ shutil.rmtree(tmpdir, ignore_errors=True)
def get_parser():