[Git] git repository size を削減する

「This repository’s size is over 1 GB.」と Bitbucket に注意されました。

2014-09-23_git_01

limit があるので 1GB を越えたところで警告するよ、ということのようです。

限定的ではありますが、対応してみました。
今後のために作業手順をメモしておきます。

状況

どれくらいの容量を使っているのかを調べる方法ですが、こちらに紹介されているので確認してみます。

size-pack がでかいのが問題のようです。

% git count-objects -v
count: 264
size: 43288
in-pack: 1394
packs: 1
size-pack: 998103
prune-packable: 0
garbage: 0
size-garbage: 0

git gc コマンドがあったよねと言うことで、試してみます。

% git gc
Counting objects: 1612, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (757/757), done.
Writing objects: 100% (1612/1612), done.
Total 1612 (delta 915), reused 1344 (delta 812)
Removing duplicate objects: 100% (256/256), done.

確認すると size は減りましたが、size-pack は増えました。
過去にコミットした大きなサイズのファイルが問題のような気がします。。。

% git count-objects -v
count: 5
size: 3768
in-pack: 1612
packs: 1
size-pack: 1005953
prune-packable: 0
garbage: 0
size-garbage: 0

過去の悪行を確認したい場合は、こちらで確認できます。。。

% git log --stat

このあたりを手がかりに何とかしてみます。

対象ファイルを洗い出す

サイズの大きいファイルを見つけるシェルスクリプトがあるよ、ということなので使わせて頂きます。

% mv ~/Downloads/git_find_big.sh ./
% chmod 777 git_find_big.sh

.git/objects が大きくなってしまっているのか。

% du -hs .git/objects
986M    .git/objects

シェルスクリプトを実行します。
思い当たるものばかり。

% ./git_find_big.sh
All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file.
size    pack    SHA                                       location
351154  246494  2762739302d105a120661d098d5ea5ee42efde2b  docs/psd/top.psd
328248  230949  26d7415a0e59bd252b1fbabce5ecd7e99c34cecc  docs/psd/**.psd
167204  115206  224299c2ef8aa09c6ad31784c43f74137cd871be  docs/psd/**.psd
164963  115923  a03394edaf3275362037878134d06cd4bae67e04  docs/psd/**.psd
163689  110466  ddb957fa00c95d62fd17de1597238b9924817e5c  docs/psd/**.psd
85331   59402   4b48de6fcac3403ef59bafffa10ed2e7f7a90659  docs/psd/menu.psd
60383   40604   ca55a6873447ef856294b825e8b1e7111262df32  docs/psd/**.psd
57028   37763   172d017b83ae5cc01783c2e346670de5463f07ac  docs/psd/**.psd
11501   11482   244e361c9b78f13e02a15dc69d4060deef6ed75b  archive.zip
5004    4992    9c59e15d9218bc3d49891cd61d7fe3f831cc9737  archive.zip

.psd ファイルを削除したいですね。

準備と問題点

.git/objects からファイルを削除するには前回の手順だけでは足りず、git filter-branch で歴史を書き換える必要があります。

準備

次節以降の手順で進めましたが、git の知識不足で何度もやり直しが発生しました。
望むような結果を得るために try & error したところ、事前に以下の整理を行う必要がありました。

  • branch は merge して master にまとめる。
  • tag は一度消して、後で付け直す。
  • 別途、git clone して作業する。

問題点

以下、理解できていない点。

  • branch や tag で分岐する前のコミットに削除したいファイルが含まれている場合、別の場所にも残っていると思われるファイルは後述手順だけでは解決できません。
  • チェックサムが変わってしまうので、共同作業とかでは影響が大きそう。
  • push -f してもリモートリポジトリが書き換わらないので、削除して作り直しました。
  • 今回は未使用で確認できなかったのですが issue がどうなるのか。

作業手順

branch や tag を整理し、リモートへ push しておきます。
リモートからこのリポジトリを git clone で作業用リポジトリとして別途ローカルに落とします。

% git checkout master
% git gc --auto

大きなサイズのファイルを削除する

ここから「Remove large files Option 1: Delete files by name」を進めます。

% git filter-branch --index-filter 'git rm --cached --ignore-unmatch docs/psd/top.psd' HEAD
Rewrite f51f68b5f659046462d129c1934f17bc4db6ed30 (114/131)rm 'docs/psd/top.psd'
Rewrite 679f364ba5efc3f2da38cd0bfef5f2f66f47711a (115/131)rm 'docs/psd/top.psd'
Rewrite 7cc5632a4e8104d16070100b89cc05912cc0e066 (116/131)rm 'docs/psd/top.psd'
Rewrite 6fbe99c42c0aaf4eb78770d4168bda610e73d3aa (117/131)rm 'docs/psd/top.psd'
Rewrite dff043ddd6660f8837cabfdd858f61e49938064d (131/131)
Ref 'refs/heads/master' was rewritten

これを対象ファイル毎に繰り返します。
下記のエラーが出る場合は、上記のコマンドに -f オプションを付けます。

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f

下記を実行します。
(パイプの左側の部分で何もリストアップされないようで、結果、何も実行されていないような記憶があるのですが、試行錯誤の最中、きちんとログがとれていませんでした。)

% git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

さらに reflog を消します。

% git reflog expire --expire=now --all

git gc を実行します。
(実際の作業では、この段階で削減できず、リモートとローカルのブランチとタグを削除してから再実行しています。)

% git gc --prune=now
Counting objects: 1572, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (661/661), done.
Writing objects: 100% (1572/1572), done.
Total 1572 (delta 870), reused 1552 (delta 868)

サイズが削減できたことを確認できました。

% du -hs .git/objects
 46M    .git/objects

リモートリポジトリを作成し直す

続けて以下を実行しましたが、リモートのリポジトリ容量は変わりませんでした。

% git push --all --force

ブラウザからリポジトリを削除し、新しく作成したローカルリポジトリを push し直します。

% git push -u origin --all
% git push -u origin --tags

元のリポジトリをリモートリポジトリの内容へ変更する

あとはリモートの origin/master と差分がないことを確認しつつ、元のローカルリポジトリへ fetch -> diff -> merge します。

マージ後、元のリポジトリのサイズを確認するとまだ大きいままです。

% du -hs .git/objects
1008M   .git/objects

reflog の掃除をして、git gc を実行します。

% git reflog expire --expire=now --all
% git gc --prune=now
% du -hs .git/objects
 54M    .git/objects

最後に git clone した作業用リポジトリを削除して終了しました。