大きなファイルや認証キーを Git にコミットした場合に履歴を削除する - bfg-repo-cleaner

Git の public リポジトリへ誤って大きなファイルをコミットしたり、認証キーやパスワードのような機密情報を登録したりした場合、ファイルを削除するだけでなくコミット履歴からも消したくなることがある。そのような場合に使える bfg-repo-cleaner を紹介する。

インストール

BFG は Java で開発されており、JRE Java 8 以上が必要で、jar ファイルとして配布されている。

jar バイナリは Maven Central に保存されているため、次のコマンドで取得するか、ブラウザから直接アクセスしてダウンロードできる。

$ curl -o bfg.jar -L https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar

使い方

リポジトリを clone する

まず、次のように --mirror フラグを指定してリポジトリの新しいコピーを clone する。

$ git clone --mirror git://example.com/some-big-repo.git

この方法で clone すると、そのリポジトリはベアリポジトリになるため通常のファイルは表示されないが、リポジトリの Git データベース全体のコピーになる。万一に備えて、この時点でバックアップしておく。

リポジトリを整理する

次に BFG を実行してリポジトリを整理する。

$ java -jar bfg.jar --strip-blobs-bigger-than 100M some-big-repo.git

この例では大きなファイルを削除する方法を示している。機密ファイルの削除や、機密情報を含むコミット履歴の変更は、下の使用例を参照する。

変更内容をリポジトリへ反映する

BFG はコミット、すべてのブランチ、タグを更新するが、不要なオブジェクトを物理的には削除しない。リポジトリを確認して履歴が更新されていることを確認したら、標準の git gc コマンドで不要な dirty data を削除する。

$ cd some-big-repo.git
$ git reflog expire --expire=now --all && git gc --prune=now --aggressive

リモートリポジトリへ反映する

最後に、更新後のリポジトリの状態に問題がなければ再度 push する。clone 時に mirror フラグを使っているため、この push はリモートサーバー上のすべての --mirror 参照を更新する。

$ git push

すべて新しい clone に置き換える

これで、全員の古いリポジトリコピーを削除し、新しい clone に置き換えればよい。古い clone から新しく整理したリポジトリへ dirty history が再投入される危険があるため、以前の clone はすべて削除するのが望ましい。

使用例

機密ファイルを削除する

ファイル名が id_rsa または id_dsa の SSH 秘密鍵ファイルをすべて削除する。

$ java -jar bfg.jar --delete-files id_{dsa,rsa}  my-repo.git

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

50 MB より大きいすべての Blob (Binary Large Object) を削除する。

$ java -jar bfg.jar --strip-blobs-bigger-than 50M  my-repo.git

機密情報を含むコミット履歴を変更する

認証キーやパスワードなどの機密情報を含めてコミットしてしまった場合は、bfg コマンドの -rf, --replace-text {filename} オプションで削除できる。

$ java -jar bfg.jar --replace-text passwords.txt  my-repo.git

ファイルに列挙したすべての秘密文字列に regex: を接頭辞として付けると正規表現として扱える。デフォルトでは秘密文字列は ***REMOVED*** に置換される。

passwords.txt の例は次のとおりである。

PASSWORD1                       # Replace literal string 'PASSWORD1' with '***REMOVED***' (default)
PASSWORD2==>examplePass         # replace with 'examplePass' instead
PASSWORD3==>                    # replace with the empty string
regex:password=\w+==>password=  # Replace, using a regex
regex:\r(\n)==>$1               # Replace Windows newlines with Unix newlines

参考