3 """Repack git repos fully the way I like them."""
5 from __future__ import print_function
17 """Return dict mapping path to its type"""
19 with open('/proc/mounts') as fp:
26 def find_git_dir(path):
27 """Try to find the .git dir to operate on"""
29 real_path = path = os.path.realpath(path)
32 if os.path.isdir(os.path.join(path, '.git')):
33 curr_path = os.path.join(path, '.git')
35 if (os.path.isdir(os.path.join(curr_path, 'refs')) and
36 os.path.isdir(os.path.join(curr_path, 'objects')) and
37 os.path.isfile(os.path.join(curr_path, 'config'))):
40 path = os.path.dirname(path)
43 raise ValueError('could not locate .git dir: %s (%s)' %
44 (orig_path, real_path))
48 """Find a good temp dir (one backed by tmpfs)"""
53 tempfile.gettempdir(),
55 mounts = mount_settings()
56 for path in SEARCH_PATHS:
57 if mounts.get(path) == 'tmpfs':
63 """Read |path| and return its data"""
64 if os.path.isfile(path):
65 return open(path).read()
70 """Unlink |path| if it exists else do nothing"""
71 if os.path.isfile(path):
75 def clean_hooks(path):
76 """Strip out sample files from hooks/"""
77 hooks_path = os.path.join(path, 'hooks')
78 for hook in glob.glob(os.path.join(hooks_path, '*.sample')):
79 print('Trimming hook: %s' % hook)
83 def clean_packs(path):
84 """Strip out temp files from objects/packs/"""
85 packs_path = os.path.join(path, 'objects', 'packs')
86 for pack in glob.glob(os.path.join(packs_path, 'tmp_pack_*')):
87 print('Trimming pack: %s' % pack)
92 """See if the git repo is already packed"""
93 if set(('info', 'pack')) != set(os.listdir(path)):
95 packs = os.listdir(os.path.join(path, 'pack'))
102 """Clean up and trim cruft and repack |path|"""
103 path = find_git_dir(path)
104 print('Repacking %s' % path)
106 tmpdir = find_temp_dir()
108 tmpdir = tempfile.mkdtemp(prefix='git-repack.', dir=tmpdir)
109 print('Using tempdir: %s' % tmpdir)
114 # Push/pop the graft & alternate paths so we don't read them.
115 # XXX: In some cases, this is bad, but I don't use them that way ...
116 graft_file = os.path.join(path, 'info', 'grafts')
117 grafts = readfile(graft_file)
120 alt_file = os.path.join(path, 'objects', 'info', 'alternates')
121 alts = readfile(alt_file)
126 origin_path = os.path.join(path, 'refs', 'remotes', 'origin')
127 packed_refs = readfile(os.path.join(path, 'packed-refs'))
128 if os.path.exists(origin_path) or 'refs/remotes/origin/' in packed_refs:
129 cmd = ['git', '--git-dir', path, 'remote', 'prune', 'origin']
130 subprocess.check_call(cmd, cwd='/')
135 print('Git repo is already packed; nothing to do')
139 print('Syncing git repo to tempdir')
140 shutil.copytree(path, tmpdir, symlinks=True)
145 cmd = ['git', '--git-dir', rundir, 'reflog', 'expire', '--all', '--stale-fix']
146 print('Cleaning reflog: %s' % ' '.join(cmd))
147 subprocess.check_call(cmd, cwd='/')
149 # This also packs refs/tags for us.
150 cmd = ['git', '--git-dir', rundir, 'gc', '--aggressive', '--prune=all']
151 print('Repacking git repo: %s' % ' '.join(cmd))
152 subprocess.check_call(cmd, cwd='/')
155 cmd = ['rsync', '-a', '--delete', tmpdir + '/', path + '/']
156 print('Syncing back git repo: %s' % ' '.join(cmd))
157 subprocess.check_call(cmd, cwd='/')
158 cmd = ['find', path + '/', '-exec', 'chmod', 'u+rw', '{}', '+']
159 subprocess.check_call(cmd, cwd='/')
163 open(graft_file, 'w').write(grafts)
165 open(alt_file, 'w').write(alts)
167 shutil.rmtree(tmpdir)
171 """Get the command line parser"""
172 parser = argparse.ArgumentParser(description=__doc__)
173 parser.add_argument('dir', help='The git repo to process')
178 """The main script entry point"""
179 parser = get_parser()
180 opts = parser.parse_args(argv)
184 if __name__ == '__main__':
185 exit(main(sys.argv[1:]))