gitの履歴から大きなファイルを探すスクリプトを書き直した

巷では、gitの履歴から大きなファイルを探す方法を紹介する記事があるが、ここに書かれているスクリプトは遅いし、bareリポジトリやsubmoduleやpackのないリポジトリでは動作しないし、looseオブジェクトからは探してくれないので、書き直してみた。

gist.github.com

僕は ~/bin にPATHを通しているので ~/bin/git-findbig に上のスクリプトを置いて chmod +x ~/bin/git-findbig している。すると以下のようにファイルを探すことができる。

$ git findbig
sha1 size size-on-disk path
ee1da0cbc84d6ccf5d0714602e4364b6b8a85a32 2103101 223238 drivers/gpu/drm/amd/include/asic_reg/bif/bif_5_1_sh_mask.h
6fa98ea0ae40f9a38256f11e5dc270363f785aee 1519697 124840 sound/soc/codecs/wm8962-tables.c
f21a8c1b9d47736fa4e27def66f04b9fe2b4bc53 1519697 83 sound/soc/codecs/wm8962-tables.c
1ddc4183a1c91cd08a05557d575b3bcdc90a1ea6 1284814 112896 drivers/gpu/drm/amd/include/asic_reg/dce/dce_11_2_sh_mask.h
9b656be7f52f9dab558a9667d3787fc9ddcf64a8 1271223 330 crypto/testmgr.h
e64a4ef9d8ca28fa1415e3e97a5d404478b89619 1270303 210 crypto/testmgr.h
acb6bbff781a2d3be030d69109912efe5e6b865f 1270201 12960 crypto/testmgr.h
4ce2d866919d79699d570be8563a97ce2af96885 1257514 7850 crypto/testmgr.h
7358931b3082cd70878046b37b15032c0a27bc26 1245290 6408 crypto/testmgr.h
78e874eca031dbf18ad3b1decf7342300df6f521 1242797 77 crypto/testmgr.h

ちなみにこれはGitHubからcloneしたばかりのlinuxリポジトリ上で実行したもので、2分17秒かかった。一般的なサイズのリポジトリなら一瞬か数秒程度で終わる。よく知られたスクリプトでは20分かかった(よく知られたスクリプトは表示する項目数に比例して時間が増大する可能性があることに注意)。

sort -k2rnsort -k3rn にすれば論理的な大きさではなくディスク上の大きさでソートされる。 head -n 10head -n 100 にすれば100件表示される。この値を増やしても処理時間はほとんど変わらない。

整形された結果が欲しければ git findbig | column -t とやるとそれっぽくなる。左詰めになってしまうが……

パスが表示されないときは、そのファイルはbranchやtagなどのrefから到達できないところにある。もしかしたら既にgit gcで削除できる状態かもしれない。

このスクリプトはbareリポジトリでも動作する。またsubmodule内でも動作する(submoduleに対して再帰的に検索するわけではない)。gitのobjectはpackedとlooseの2種類のうちどちらかの方法で保管されているが、このスクリプトは両方のobjectを対象に検索する。packed objectが1つもないリポジトリでも正しく動作する。