大きくなってしまった git リポジトリへの対抗策

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

ギョームで使ってるあるシステムのリポジトリが大きすぎて、クローンめっちゃつらい、というかできないんだけど!となったので調べた。

git リポジトリの肥大化は、長年積み重なったログ、うっかりコミットされた大きなファイル、バイナリファイルの積み重ねが主な原因っぽい。
(バイナリファイルは差分でログにしていくのではなく、フルでログになる)

なので、大きく 2 つの方向があると思う。
1 つめはリポジトリ自体のログをねじ曲げる方向。ログが原因なので、そのログから原因要素を取り除けばいいじゃん!という理解。
もう 1 つはクローンするときに工夫する方向。ログが大きすぎるのでログを最小限にしていく。

方法 1 : ログを書き換える

方法 1-a : リポジトリを作り直す

リポジトリのログをねじ曲げるにあたって、手っ取り早い改善策はリポジトリを作り直すこと。最新の状態を手元に持っておき、リポジトリを捨て、最新の状態でファーストコミットをする。もろもろの再設定の手間をかけられる、ログや Github などのリポジトリに紐づく情報はポイしてもよい、そんな決断ができるなら一番簡単で効果も高い方法です。

ただ、これからクローンするのはを楽にしたいんだけど!という用途にはちょっと違うかも。

方法1-b : filter-branch サブコマンドを使って不要なファイルを歴史から隠蔽する

filter-branchを使うと、ログを遡ってコマンドを適用していけます。これを使ってログに潜む悪のファイルを精査します。ただあんまりつかったことないのでわからんね、かなしみ。

Git - 歴史の書き換え

全コミットからのファイルの削除

$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten

こういう使い方で、まあまあ、簡単に捨てられそうですね。

試しに適当なリポジトリで実行してみたら、キレイサッパリとファイルが消え去りました。ちなみにコミットハッシュも変わるようです(そりゃそうだ)

$ git log --oneline --graph
* 94a3acd (HEAD -> master) ...
* c5615d4 ...
* 1b58a6d ...
* 958f4b0 ...
* 6aeccd4 ...
* 2d39eca ...
* b22e882 ...
* 554d930 ...
* d620f96 ...
* d0a5a86 ...

$ git filter-branch -f --tree-filter "rm -f package.json"
Rewrite 94a3acd99880461b586931f4d4a283494d3f9fcc (8/10) (3 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten

$ git log --oneline --graph
* ccd7b67 (HEAD -> master) ...
* 5159356 ...
* 5551da3 ...
* 862c676 ...
* c1f6cb5 ...
* 3f1eb70 ...
* 36bc53c ...
* d7c2fa6 ...
* d620f96 ...
* d0a5a86 ...

ということで、方法1はどちらもログをぶっ壊していくので、コミットハッシュが変わります。自分も含め、既にクローンなどによってリポジトリを追跡してた人は、整合性が取れなくなるので、再度ローカルのリポジトリ状況を刷新する必要がありそうです。 fetch しよう!とかそういう話じゃなくて、過去のログを持ち込まないとか、そういうところ。

方法 2 : クローンするときに工夫する

方法 2-a : Shallow Clone

Shallow Clone の方法の1つとして depth というプロパティが設定でき、ログをどこまで遡ってクローンするかを決められます。


$ git clone --depth ...

これをするとログの遡りが少なくなるので、その分早くクローンをすることができます。かなり大きくなったリポジトリで試したらこれくらいの違いでした。 およそ 1 分かかっていたものが 30 秒程度にまとまって非常にすばらしい。

$ time git clone ...
Initialized empty Git repository in ...
remote: Counting objects: 137514, done.
remote: Compressing objects: 100% (31116/31116), done.
remote: Total 137514 (delta 108680), reused 129419 (delta 102311)
Receiving objects: 100% (137514/137514), 215.85 MiB | 8.55 MiB/s, done.
Resolving deltas: 100% (108680/108680), done.

real    1m1.198s
user    0m19.858s
sys     0m6.843s


$ time git clone --depth 1 ...
Initialized empty Git repository in ...
remote: Counting objects: 10862, done.
remote: Compressing objects: 100% (6934/6934), done.
remote: Total 10862 (delta 4852), reused 8236 (delta 3409)
Receiving objects: 100% (10862/10862), 109.83 MiB | 7.15 MiB/s, done.
Resolving deltas: 100% (4852/4852), done.

real    0m27.406s
user    0m6.650s
sys     0m2.897s

足りないログについては git fetch --depth で値を増やすと補完できるっぽい。

$ git log --oneline | wc -l
1

$ git fetch --depth 10000
remote: Counting objects: 126652, done.
remote: Compressing objects: 100% (22233/22233), done.
remote: Total 126652 (delta 103609), reused 123617 (delta 100851)
Receiving objects: 100% (126652/126652), 106.69 MiB | 5.92 MiB/s, done.
Resolving deltas: 100% (103609/103609), completed with 1886 local objects.

$ git log --oneline | wc -l
13101

Shallow Clone したリポジトリは、そういうリポジトリになるようで、深さを再設定しないと永遠とログが出てこないそうです。ちょっと昔までは push と pull もままならなかったようですが、今は改善されたようです。

いつのまにかGitのshallow cloneが”Push”も"Pull"もできるように超進化していたよ! すごーい! - ブログなんだよもん


とりあえず手元でこのブランチの様子を検証したいんやけど!みたいなものなんかは --depth 1 を積極的に使っていくとよさげたん CI ツールの類もクローンするときは全部これでええやんけ。


そのほか勉強になった記事

[Git] git repository size を削減する | deadwood

巨大なリポジトリ を Git で上手く扱う方法 | Atlassian Blogs

この記事はどうでしたか

前後の記事

Next:
Prev: