Git
filter-branch

gitのレポジトリ容量の削減に失敗しないためのチェックポイント

gitのレポジトリ容量の削減に失敗しないためのチェックポイント

git cloneすると重すぎるから、レポジトリ容量の削減せねばと思っていましたが、「めんどくさい&歴史改変して、ファイル抹消するの怖い」という理由で、なかなか手をつけてませんでした。
実際に思い切ってやったところ、スッキリしたので、まとめておきます。

gitのレポジトリ容量を削減する

全体像の把握

基本的には、 https://gist.github.com/ktx2207/3167fa69531bdd6b44f1 が簡潔にまとまっていますが、ちょいちょいハマりどころがあったので、全体像を把握するためにまとめておきます。

# バックアップ処理
# gitの容量確認
du -sh .git/objects
# Gitオブジェクトが管理しているファイルの容量を降順でリストとして出力する
bash ./git_find_big.sh

# filter-branchでコミットの歴史を改変する http://dskd.jp/archives/46.html も参考に!
## --tag-name-filter cat .. タグも履歴を変更する
## -- .. ハイフン2つで前のコマンドのオプションから抜ける
## --all .. 全てのブランチを改変する
git filter-branch -f --index-filter --tag-name-filter cat \
  "git rm -rf --cached --ignore-unmatch XXX/ YYY/img/*.jpg" \
  --prune-empty -- --all
# https://gist.github.com/ktx2207/3167fa69531bdd6b44f1 を参考
## reflog削除 (reflogについては、https://gist.github.com/kymmt90/9c997726b638b316f9be07aa4e3eea5e が詳しい)
git gc --aggressive --prune=now

# 成功したか、確認する。filter-branchで指定したファイル、ディレクトリが削除されていれば、
# うまくいっていなければ、最初からやり直すのが無難だと思う。
bash ./git_find_big.sh
du .git/objects

# reflogを削除する
git reflog expire --expire=now --all


# ブランチ全てを改変
git push origin --all --force
# https://help.github.com/articles/removing-sensitive-data-from-a-repository/#using-filter-branch の7番参考
git push origin --force --tags

以下、注意すべき点です:sunny:

チェックポイント

バックアップを取ろう

を参照のこと. また、全てのブランチを変更するため、git cloneで別ディレクトリで処理を行う方が良いでしょう。
https://developers.eure.jp/tech/alternative-git-history/ もバックアップの重要性について強調しています。

.git/objects のファイルサイズを測りながら、git_find_big.shで消すべきファイルを精査しよう

git_find_big.shとは、
履歴にある(Gitオブジェクトが管理している)ファイルの容量を降順でリストとして出力してくれるスクリプトです. (https://developers.eure.jp/tech/alternative-git-history/ のgit_find_big.shスクリプト実行の節を参照すること。git_find_big.shはダウンロードしに行きましょう)
このスクリプトと、du -sh .git/objectsを使って、git オブジェクトの容量を確認しつつ、容量を圧迫しているファイル群を探りに行きます:

# バックアップ処理(下記)
du -sh .git/objects
bash ./git_find_big.sh

filter-branch のオプションを理解しよう

filter-branchとは、コミットの歴史を一括変更したり、抹消したりできるコマンドで、バージョン管理そのものの理念を覆してしまうという点で最強と名高い?やつです。 http://dskd.jp/archives/46.html
歴史の書き換えはたったの数個のコマンド実行で済む。の節の説明が簡潔で良いです1
上記のリンク以外の注意点としては、一つだけ。
もし、gitのタグを扱っていれば、タグの歴史を変更する--tag-name-filter catfilter-branchの後ろに付け加えます。

コミットを圧縮して、ゴミをなくそう

git gcにより、過去のコミットを圧縮し、容量を減らします:

# filter-branchの処理
# git gcする
git gc --aggressive --prune=now
# reflogを削除する(reflogとは、リポジトリにやった全ての変更を――コミット群の形で――記録するある種のメタリポジトリ)
git reflog expire --expire=now --all

処理したファイルがきちんと削除できたか確認しよう

上記のコマンドを用いて、filter-branchして、成功しているか確認します。何らかの原因でうまくいってなさそうならば、最初からやり直すのが無難かもしれません。
(別ディレクトリで作業していて、まだリモートにpushしていない以上、他のユーザーに影響することはなく、かつ自身も無傷なわけですから、ここの段階での失敗は恐れずに行きましょう! 最悪、また一からやり直せばいいわけですし:hamster: )

# コミットの歴史を改変する
## filter-branchの処理
## reflog, gc処理

# 成功しているか、確認する
bash ./git_find_big.sh
du .git/objects

タグも忘れずにpushしよう

いよいよ、リモートにpushします。この時に、タグを扱っている場合は、branchのpushの他に、タグの変更をpushする必要がある(具体的には、git push origin --force --tags)ことを忘れずに!
(ちなみに、僕はtagのpushを忘れて少しハマってしまいました。https://help.github.com/articles/removing-sensitive-data-from-a-repository/#using-filter-branch の7番を見て気づきました:ok_hand_tone1: )

git push origin --all --force
# https://help.github.com/articles/removing-sensitive-data-from-a-repository/#using-filter-branch の7番参考
git push origin --force --tags

これが終われば、歴史を改変した後のレポジトリをgit cloneして、容量が削減されたことを念のため、確認すれば良いでしょう:bulb:

全てのブランチの歴史を改変するのは、未経験だと勇気がいる?作業だと思うのですが、上記の注意点に気をつけて確認しながらやっていくことで、成功することを祈っています:sunflower:


  1. より詳細なオプションの説明については、 https://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html を参照のこと