- 更新日: 2016年7月27日
- Git
Git の履歴からファイルを完全に削除する – git filter-branch
Git の履歴を過去に遡って一括で改変するには、git filter-branch というコマンドが使えるそうです。Rails4.1 にアップデートしてから、config/secrets.yml を .gitignore に追加し忘れていて、どんどん commit を進めてしまっていたのですよね…
ということで、config/secrets.yml を git の履歴から完全に削除するために、git filter-branch を使いました。いきなり master ブランチじゃなくて、まずは別にブランチを切って上手くいくかどうか試す。さらに心配なら別ディレクトリに git clone して試すとか。
1 2 3 |
$ git checkout -b clean-secret-files |
git の履歴とワークツリーの両方から削除
ファイルを削除。git の履歴からもワークツリーからも削除します。
1 2 3 |
$ git filter-branch -f --index-filter 'git rm --ignore-unmatch filename' HEAD |
ディレクトリを削除。rm に -rf オプションを付けます。
1 2 3 |
$ git filter-branch -f --index-filter 'git rm -rf --ignore-unmatch dirname' HEAD |
git の履歴を削除してワークツリーには残す
コミット履歴は削除して、ワークツリーのファイルを残したい場合は、–cached オプションを付加します。
ファイル履歴の削除。
1 2 3 |
$ git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch filename' HEAD |
config/secrets.yml に対しては、実際にはこのコマンドを使いました。ワークツリーにファイルを残したかったですので。config/secrets.yml の git 履歴を削除。
1 2 3 |
$ git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch config/secrets.yml' HEAD |
ディレクトリ履歴の削除。
1 2 3 |
$ git filter-branch -f --index-filter 'git rm -rf --cached --ignore-unmatch dirname' HEAD |
以上の git filter-branch コマンドを実行すると、対象のファイル・ディレクトリが存在するコミットで、がーっと rm が進んでいきます。
実際には config/secrets.yml 以外にも、間違えて commit していた巨大な SQL ファイルなども削除しました。
master ブランチで同じ作業を行う
clean-secret-files ブランチで git filter-branch による作業が上手く行ったことを確かめた後、テストが全部通ることも確かめた上で、master ブランチで同じ作業を行いました。
1 2 3 4 5 |
$ git checkout master $ git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch config/secrets.yml' HEAD $ git push --force origin master |
リモートのリポジトリに push する時は、履歴を全部書き換えるために –force オプションを付けます。ただし、force オプションを付けての push は履歴を全て書き換える点に注意です。自分しか使わない個人プロジェクトのリポジトリなら問題ないですが、チームで共有しているリモートリポジトリの場合はあらかじめメンバー間での同意が必要かと思います。大きなチームの場合は無理かもしれません。
補足1: –tree-filter オプション
–tree-filter オプションを使う方法もあるそうです。
1 2 3 4 |
$ git filter-branch --tree-filter 'rm passwords.txt' HEAD $ git filter-branch --tree-filter 'rm -rf path/to/dir' HEAD |
–index-filter と –tree-filter のオプションの違いは、ツリーを check out するかどうからしく –index-filter のほうが高速らしい。
補足2: BFG Repo-Cleaner
BFG Repo-Cleaner という git flter-branch を使いやすくしたツールがあることを、その作者さんから Twitter で教えてもらいました。
・Removing Crazy Big Files
・Removing Passwords, Credentials & other Private data
週末試してみようと思います。
【追記 2016/07/27】
だだ星人さんに、コメントを頂きました。git filter-branch を実行しただけでは、git リポジトリの .git フォルダのサイズは減りません。.git フォルダのサイズを減少させるためには、git filter-branch による悪影響がないことを確認した後に、別にコマンドを実行する必要があります。詳細はコメントをご覧ください。
【追記ここまで】
- – 参考リンク –
- git-filter-branch(1)
- gitで特定のファイルの履歴を消す方法 – おし、プログラミング
- Git – 歴史の書き換え
- transitive.info – git filter-branch 使い方
- Git の関連記事
- git cleanでUntracked files(未追跡ファイル)をまとめて削除
- .gitignoreを自動生成するgiboとgi(gitignore.io)コマンド
- git管理ファイルを.gitignoreに追加してgit管理から外す
- gitマージのコンフリクトで片方ブランチのファイル変更内容を採用
- git logコマンドで他ブランチにマージされていないコミットのみを確認
- Bitbucketで作成済みWikiページの一覧リストを確認
- GitHubのgh-pages (github.io)でWebページを公開
- git diffで長い行を折り返し表示
- git diffでブランチ間のファイル差分を確認するあれこれ
- Bitbucketアカウント作成〜ローカルのGit既存プロジェクトをインポート(push)
- 初回公開日: 2014年6月27日
- 4件のコメント
情報ありがとうございます。
リポジトリサイズ圧縮情報を探してこちらをみつけました。
当方のプロジェクト、不要なフォルダが1.2GBあり(リビジョンは2000超)、これが削除できれば、と思っていたのです。
不要なフォルダは削除できました。
ところがリポジトリの.gitフォルダサイズは変化無しです(1.2GB減ることを期待していました)。
リポジトリサイズまでは変わらない物でしょうか?
だだ星人さん、こんにちは。コメントありがとうございます。
git filter-branch は .git フォルダにオリジナルの履歴参照のためのバックアップを作成するらしく、.git フォルダ(.git/objects)のサイズ削減はしないようです。
ですので git filter-branch を実行しただけで .git フォルダのサイズが減らないのは、正常な状態となります。
GitHub に情報が掲載してあります。
https://help.github.com/articles/remove-sensitive-data/
上記ページに書いてありますが .git フォルダのサイズを減らすには、git filter-branch 実行後に次のコマンドを実行する必要があります。
$ git for-each-ref –format=’delete %(refname)’ refs/original | git update-ref –stdin
$ git reflog expire –expire=now –all
$ git gc –prune=now
ただし、このコマンドには以下の注意が書いてありました。
“After some time has passed and you’re confident that git filter-branch had no unintended side effects, you can force all objects in your local repository to be dereferenced and garbage collected with the following commands”
訳)しばらくして git filter-branch による悪影響がないと確信できたら、このコマンドでローカルリポジトリのオブジェクトへの参照を削除してガベージコレクトできます。
実際にテスト用のリポジトリで試したところ、上記コマンドを実行したら .git フォルダのサイズが減ることが確認できました。
また Atlassian のページにも同様の情報がありました。
https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html#Reducerepositorysize-Deletefilesbyname
GitHub のとは少し違いますが、同様に git for-each-ref で refs/original/ 以下のファイルを削除するコマンドが紹介されています。
$ git for-each-ref –format=”%(refname)” refs/original/ | xargs -n 1 git update-ref -d
こちらにも同じような注意書きがありました。
“filter-branch creates backups of your original refs namespaced under refs/original/. Once you’re confident that you deleted the correct files, you can run the following command to delete the backed up refs, allowing the large objects to be garbage collected”
訳)git filter-branch は .git/refs/original/ 以下にオリジナル参照のバックアップを作成します。git filter-branch で正常なファイルを削除できたと確認した後に、次のコマンドを実行することで、バックアップの参照を削除してサイズの大きいオブジェクトをガベージコレクトさせることができます。
.git/refs/original/ 以下の .git/objects へのバックアップ参照を削除することで、.git/objects 以下に格納された大きなファイル(オブジェクト)がガベージコレクトの対象となり削除される、という動作になるのかな〜と思います。
以上ですが、ご参考になれば幸いです!
なんと!
ご丁寧に記述していただきびっくりです。
早速試してみたいと思います。ありがとうございました。
いえいえ、どういたしまして!
私も余り詳しくない部分だったので、ちょっと興味を持ちまして調べた次第です。
git は奥が深いですね。