]>
git.wh0rd.org - home.git/blob - .bin/git-repack
3 # pylint: disable=fixme,invalid-name
4 # pylint: disable=too-many-branches,too-many-locals,too-many-statements
6 """Repack git repos fully the way I like them."""
8 from __future__
import print_function
20 """Return dict mapping path to its type"""
22 with
open('/proc/mounts') as fp
:
30 """Whether |path| is a .git dir"""
31 return (os
.path
.isdir(os
.path
.join(path
, 'refs')) and
32 os
.path
.isdir(os
.path
.join(path
, 'objects')) and
33 os
.path
.isfile(os
.path
.join(path
, 'config')))
36 def find_git_dir(path
):
37 """Try to find the .git dir to operate on"""
39 real_path
= path
= os
.path
.realpath(path
)
42 if os
.path
.isdir(os
.path
.join(path
, '.git')):
43 curr_path
= os
.path
.join(path
, '.git')
45 if is_git_dir(curr_path
):
48 path
= os
.path
.dirname(path
)
51 raise ValueError('could not locate .git dir: %s (%s)' %
52 (orig_path
, real_path
))
56 """Find a good temp dir (one backed by tmpfs)"""
61 tempfile
.gettempdir(),
63 mounts
= mount_settings()
64 for path
in SEARCH_PATHS
:
65 if mounts
.get(path
) == 'tmpfs':
71 """Read |path| and return its data"""
72 if os
.path
.isfile(path
):
73 with
open(path
) as fp
:
79 """Unlink |path| if it exists else do nothing"""
80 if os
.path
.isfile(path
):
84 def clean_hooks(path
):
85 """Strip out sample files from hooks/"""
86 hooks_path
= os
.path
.join(path
, 'hooks')
87 for hook
in glob
.glob(os
.path
.join(hooks_path
, '*.sample')):
88 print('Trimming hook: %s' % hook
)
92 def clean_packs(path
):
93 """Strip out temp files from objects/packs/"""
94 packs_path
= os
.path
.join(path
, 'objects', 'packs')
95 for pack
in glob
.glob(os
.path
.join(packs_path
, 'tmp_pack_*')):
96 print('Trimming pack: %s' % pack
)
101 """See if the git repo is already packed"""
102 obj_path
= os
.path
.join(path
, 'objects')
103 paths
= set(os
.listdir(obj_path
))
104 if paths
not in ({'info', 'pack'}, {'pack'}):
106 packs
= os
.listdir(os
.path
.join(obj_path
, 'pack'))
113 """Clean up and trim cruft and repack |path|"""
114 path
= find_git_dir(path
)
115 print('Repacking %s' % path
)
117 # Repack any submodules this project might use.
118 modules_path
= os
.path
.join(path
, 'modules')
119 if os
.path
.isdir(modules_path
):
120 for root
, dirs
, _
in os
.walk(modules_path
):
123 mod_path
= os
.path
.join(root
, d
)
124 if is_git_dir(mod_path
):
127 tmpdir
= find_temp_dir()
129 tmpdir
= tempfile
.mkdtemp(prefix
='git-repack.', dir=tmpdir
)
130 print('Using tempdir: %s' % tmpdir
)
132 # Doesn't matter for these needs.
133 os
.environ
['GIT_WORK_TREE'] = tmpdir
137 # Push/pop the graft & alternate paths so we don't read them.
138 # XXX: In some cases, this is bad, but I don't use them that way ...
139 graft_file
= os
.path
.join(path
, 'info', 'grafts')
140 grafts
= readfile(graft_file
)
143 alt_file
= os
.path
.join(path
, 'objects', 'info', 'alternates')
144 alts
= readfile(alt_file
)
149 # XXX: Should do this for all remotes?
150 origin_path
= os
.path
.join(path
, 'refs', 'remotes', 'origin')
151 packed_refs
= readfile(os
.path
.join(path
, 'packed-refs'))
152 if os
.path
.exists(origin_path
) or 'refs/remotes/origin/' in packed_refs
:
153 cmd
= ['git', '--git-dir', path
, 'remote', 'prune', 'origin']
154 subprocess
.run(cmd
, cwd
='/', check
=True)
159 print('Git repo is already packed; nothing to do')
163 print('Syncing git repo to tempdir')
164 shutil
.copytree(path
, tmpdir
, symlinks
=True)
169 cmd
= ['git', '--git-dir', rundir
, 'reflog', 'expire', '--all', '--stale-fix']
170 print('Cleaning reflog: %s' % ' '.join(cmd
))
171 subprocess
.run(cmd
, cwd
='/', check
=True)
173 # This also packs refs/tags for us.
174 cmd
= ['git', '--git-dir', rundir
, 'gc', '--aggressive', '--prune=all']
175 print('Repacking git repo: %s' % ' '.join(cmd
))
176 subprocess
.run(cmd
, cwd
='/', check
=True)
179 cmd
= ['find', rundir
, '-depth', '-type', 'd', '-exec', 'rmdir', '{}', '+']
180 subprocess
.call(cmd
, stderr
=subprocess
.DEVNULL
)
182 # There's a few dirs we need to exist even if they're empty.
183 refdir
= os
.path
.join(rundir
, 'refs')
184 os
.makedirs(refdir
, exist_ok
=True)
187 cmd
= ['rsync', '-a', '--delete', tmpdir
+ '/', path
+ '/']
188 print('Syncing back git repo: %s' % ' '.join(cmd
))
189 subprocess
.run(cmd
, cwd
='/', check
=True)
190 cmd
= ['find', path
+ '/', '-exec', 'chmod', 'u+rw', '{}', '+']
191 subprocess
.run(cmd
, cwd
='/', check
=True)
195 with
open(graft_file
, 'w') as fp
:
198 with
open(alt_file
, 'w') as fp
:
201 shutil
.rmtree(tmpdir
, ignore_errors
=True)
205 """Get the command line parser"""
206 parser
= argparse
.ArgumentParser(description
=__doc__
)
207 parser
.add_argument('dir', help='The git repo to process')
212 """The main script entry point"""
213 parser
= get_parser()
214 opts
= parser
.parse_args(argv
)
218 if __name__
== '__main__':
219 exit(main(sys
.argv
[1:]))