BFG Repo-Cleanerで青空文庫のGithubレポジトリを軽くできるか試してみた

「Code for 青空文庫」アイデアソン #1に参加していました。参加してました、と言えるほど何も貢献してないんですけど。すみません。。

青空文庫については、こんなツイートがありました。たしかに青空文庫のGithubレポジトリはあって開催まで時間もあったわけで、 アイデアソンとはいえ「実際こんなのつくってみたよ!」みたいなのがもっとあってもよかった気がします。

で、「手を動かす」のを妨げてるのは何かというと、ひとつはレポジトリのサイズが気軽にcloneできる大きさじゃない、ということがあると思います。個人的にはcloneしようとしてなかなか終わらなくて挫けました。完全に言い訳ですけど。

一念発起、実際git cloneしてみて調べると、7.7GBありました。カタギの人間にはつらい重さです。

$ du -sh .
7.7G    .

その理由は、↓こういう重めの画像ファイルを含むZIPファイルがレポジトリに入っているせいではないか、という話をちらっと聞いたので、その辺をレポジトリから削除するとどれくらい軽くなるか試してみました。

図書カード:ねっこうまれのこびとたち

合計サイズ

そこまで大したサイズではなさそうです。うーん。

$ find . -name '*.zip' -exec stat --format=%s {} \; | awk '{sum+=$1}; END {print sum/1024/1024 "MB"}'
616.882MB

git filter-branch --index-filter

パスワードを含むファイルとか、やばいファイルをレポジトリに入れてしまったときは、過去のコミットからも消さないといけません。Gitにはそのためのコマンドが用意されています。これを使ってみます。

ただ、よく分かりませんがこのコマンドはたくさんのファイルを消す時には不向きのようです。

$ git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch $(find . -name '*.zip')" \
--prune-empty --tag-name-filter cat -- --all
-bash: /usr/bin/git: Argument list too long

いくつかに絞ってみても、よく分からないエラーが出ます。ひとつのファイルしか指定できないみたいです。

$ git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch $(find . -name '*.zip' | head -3)" \
--prune-empty --tag-name-filter cat -- --all
Rewrite 371bcfa53617dade8aa892cc0f23d724a7bd029e (1/1489)rm 'cards/001339/files/48380_txt_31990.zip'
/usr/lib/git-core/git-filter-branch: 2: eval: ./cards/001339/files/48381_txt_31991.zip: not found
/usr/lib/git-core/git-filter-branch: 3: eval: ./cards/001595/files/53683_txt_44483.zip: not found
index filter failed: git rm --cached --ignore-unmatch ./cards/001339/files/48380_txt_31990.zip
./cards/001339/files/48381_txt_31991.zip
./cards/001595/files/53683_txt_44483.zip

こんな感じでforで回すことはできますが、いかんせん時間かかりすぎて断念しました。たぶん丸2日くらいかかる予感。

$ for z in $(find . -name '*.zip'); do
  git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch ${z}" \
  --prune-empty --tag-name-filter cat -- --all
done

BFG Repo-Cleaner

上のGithubの記事でも紹介されてましたが、git filter-branchをもう少し楽にしたBFGというのがあるらしいです。

rtyley.github.io

これを使ってみます。

今回は消すファイルは拡張子で判別できるので、--delete-files '*.zip'を指定します。あと、安全のため最新のコミットは残すというのがデフォルトの挙動になっていますが、それも含めて消してほしいので--no-blob-protectionオプションをつけます(ドキュメントにあるみたいに、git clone --mirrorでクローンしてきた場合は不要だと思います)。git gcはけっこう時間かかります。

$ java -jar bfg-1.12.3.jar --no-blob-protection --delete-files '*.zip'
...snip...
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive

で、これで消えたかと思いきや、stageされた差分として残っています。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   cards/XXX/XXX.zip
...snip...

git reset HEADしてstageを取り消し、実際のファイルも消します。あと、.gitignoreにも入れます。

$ git reset HEAD
$ find . -name '*.zip' -delete
$ echo '*.zip' >> .gitignore
$ git add .gitignore
$ git commit -m 'ignore ZIP files'

さあ、これでどれくらい減ったかというと…

$ du -sh .
2.3G    .

うーん、1/3くらいにはなりましたが、まだ気軽にcloneできるサイズではなさそうです。

github.com

.pngとか.ebkとか色々消してみる

ここからさらに画像とかを消すと、1.1GBくらいにはなりましたが、それ以上は減らなそうでした。

上のレポジトリには反映していません。

git filter-branch --subdirectory-filter

git filter-branchには、サブディレクトリをレポジトリとして切り出すコマンドがあります。これを使ってcards/だけ切り出すとどうなるかやってみます。

$ git filter-branch --subdirectory-filter cards HEAD
Rewrite 877a7e4624ea02e890219b7210b1dc8aae851c5f (1490/1490)
Ref 'refs/heads/master' was rewritten

このディレクトリに関連するコミットだけが切り出されているので、さっき追加した.gitignoreは消えています。また作る必要があります。

これでどれくらいのサイズになるかというと…

$ du -sh .
962M    .

まだ1GB程度はあります。あんまり変わってませんでした。

注:↓のレポジトリはディレクトリ数が多いので開くとブラウザが重くなります。 github.com

感想

人間が読むために人間が書くテキストファイルなんてそんな重くならないだろう、と勝手に思ってたんですが、たとえばこの作品とか、2MBあります。

宮本百合子 道標

ということで、これ以上は軽くならなそう。

青空文庫の歴史の重みを感じました。軽く見てすみませんでした。。