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 して試すとか。

git の履歴とワークツリーの両方から削除

ファイルを削除。git の履歴からもワークツリーからも削除します。

ディレクトリを削除。rm に -rf オプションを付けます。

git の履歴を削除してワークツリーには残す

コミット履歴は削除して、ワークツリーのファイルを残したい場合は、–cached オプションを付加します。

ファイル履歴の削除。

config/secrets.yml に対しては、実際にはこのコマンドを使いました。ワークツリーにファイルを残したかったですので。config/secrets.yml の git 履歴を削除。

ディレクトリ履歴の削除。

以上の git filter-branch コマンドを実行すると、対象のファイル・ディレクトリが存在するコミットで、がーっと rm が進んでいきます。

実際には config/secrets.yml 以外にも、間違えて commit していた巨大な SQL ファイルなども削除しました。

master ブランチで同じ作業を行う

clean-secret-files ブランチで git filter-branch による作業が上手く行ったことを確かめた後、テストが全部通ることも確かめた上で、master ブランチで同じ作業を行いました。

リモートのリポジトリに push する時は、履歴を全部書き換えるために –force オプションを付けます。ただし、force オプションを付けての push は履歴を全て書き換える点に注意です。自分しか使わない個人プロジェクトのリポジトリなら問題ないですが、チームで共有しているリモートリポジトリの場合はあらかじめメンバー間での同意が必要かと思います。大きなチームの場合は無理かもしれません。

補足1: –tree-filter オプション

–tree-filter オプションを使う方法もあるそうです。

–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 による悪影響がないことを確認した後に、別にコマンドを実行する必要があります。詳細はコメントをご覧ください。
【追記ここまで】

スポンサーリンク
スポンサーリンク
 
Twitterを使っていますのでフォローお願いたします!ブログの更新情報もつぶやいてます^^
(英語学習用)
  • 4件のコメント
  • だだ星人

     情報ありがとうございます。
     リポジトリサイズ圧縮情報を探してこちらをみつけました。

     当方のプロジェクト、不要なフォルダが1.2GBあり(リビジョンは2000超)、これが削除できれば、と思っていたのです。

     不要なフォルダは削除できました。

     ところがリポジトリの.gitフォルダサイズは変化無しです(1.2GB減ることを期待していました)。

     リポジトリサイズまでは変わらない物でしょうか?

    • taka

      だだ星人さん、こんにちは。コメントありがとうございます。
      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 以下に格納された大きなファイル(オブジェクト)がガベージコレクトの対象となり削除される、という動作になるのかな〜と思います。
      以上ですが、ご参考になれば幸いです!

  • だだ星人

    なんと!

     ご丁寧に記述していただきびっくりです。
     早速試してみたいと思います。ありがとうございました。

    • taka

      いえいえ、どういたしまして!
      私も余り詳しくない部分だったので、ちょっと興味を持ちまして調べた次第です。
      git は奥が深いですね。

Leave Your Message!