【Git】間違ってコミットしたファイルを歴史から完全に抹消する方法(git filter-repo)
今回は、Gitリポジトリの「大掃除」についての技術的な解説です。
開発に夢中になっていると、うっかり「入れてはいけないもの」をコミットし、リモートにプッシュしてしまうことがあります。
- 機密情報: secret.txt、APIキー、.env ファイル。
- リポジトリの肥大化: テスト用の巨大な動画ファイル、圧縮zip、node_modules。
慌てて git rm で削除してコミットし直しても、それは「削除したという履歴」が追加されただけです。過去のコミットログ(.git ディレクトリの中)には、そのファイルのデータが厳然として残っています。
リポジトリのサイズは減りませんし、履歴を遡れば中身は見えてしまいます。
今回は、Git公式推奨のモダンなツール git-filter-repo を使い、特定のファイルを「歴史上、最初から存在しなかったこと」にする外科手術の手順を解説します。
目標:歴史の改変と強制同期
今回のゴールは以下の通りです。
- 完全消去: 過去の全てのコミットから、指定したファイル(今回は例として secret.txt)を物理的に消滅させる。
- 歴史の書き換え: ファイル削除に伴い、コミットハッシュ(ID)を再計算して歴史を整形する。
- 強制上書き: 既に汚れてしまったリモートリポジトリ(GitHub/GitLab等)の main ブランチを、きれいな歴史で上書きする。
準備:最強の掃除機 git-filter-repo
かつては git filter-branch や BFG Repo-Cleaner が使われていましたが、現在は git-filter-repo が公式推奨です。Python製で動作が非常に高速であり、複雑な履歴でも安全に処理できるよう設計されています。
インストール(Python環境が必要):
pip install git-filter-repo
※ MacでHomebrewを使っている場合は brew install git-filter-repo でも入ります。
【警告】作業前のバックアップ
これから行うのは破壊的な操作です。
万が一、必要なファイルまで消してしまった場合に備え、必ずリポジトリのディレクトリごとコピーしてバックアップを取ってください。
実践手順:特定ファイルの抹消
今回は、リポジトリに誤って混入した secret.txt を消し去る手順を進めます。
※巨大な動画ファイルやバイナリデータの場合も、ファイル名を変えるだけで手順は全く同じです。
存在確認
まず、そのファイルがGitの歴史の中にどれくらい深く根を張っているか確認します。
git log --all -- secret.txt commit 476b4bcd36ee3e1b900db9edb98ea82e12d61f90 (HEAD -> master) Author: nando <xxx@gmail.com> Date: Wed Feb 11 18:51:51 2026 +0900 DELETE secret.txt commit c0676094dab6734d147f14273275340655d5f513 Author: nando <xxx@gmail.com> Date: Wed Feb 11 18:51:40 2026 +0900 ADD secret.txt
ログが表示される場合、Gitはそのファイルを記憶しています。
歴史からの抹消(実行)
以下のコマンドを実行します。これが手術の執刀です。
git filter-repo --path secret.txt --invert-paths
オプション解説:
- –path secret.txt: 消したいファイルを指定します(ディレクトリごとならディレクトリ名を指定)。
- –invert-paths: これが重要です。「指定したパス**以外(invert)を残す」=「指定したパスを捨てる」**という指示になります。
ここでエラーが出た場合
もし Aborting: Refusing to destructively overwrite repo history... というエラーが出たら、それは「安全装置」です。
git filter-repo は、誤操作を防ぐために「クローンした直後のきれいな状態」でないと実行を拒否することがあります。
その場合は、–force オプションを付けて強制実行してください。
git filter-repo --path secret.txt --invert-paths --force git log commit 60219177b930da26f899b0675a2b8a67fdbc3b3c (HEAD -> master) Author: nando <xxx@gmail.com> Date: Wed Feb 11 18:51:13 2026 +0900 initial commit git log --all -- secret.txt <何も出なければok>
実行後、一瞬で処理が完了します。再度 git log で確認し、何も出なければローカルリポジトリは浄化されました。
難関:保護されたリモートへの反映
ここからが、多くの人が躓くポイントです。
ローカルはきれいになりましたが、リモート(GitHub等)にはまだ「汚れた歴史」が残っています。さらに、このファイルが既にマージされて main ブランチに入り込んでいる場合、事態は少し複雑です。
通常、main ブランチは誤操作防止のために「保護(Protected)」されており、歴史を改変する Force Push(強制プッシュ)は拒否される設定になっているからです。
このまま git push origin main –force を打っても、以下のようなエラーで弾かれます。
! [remote rejected] main -> main (protected branch hook declined)
これを突破するには、管理者権限による一時的なルール解除が必要です。
保護ルールの解除
GitHubやGitLabのリポジトリ設定画面(Settings -> Branches)を開き、main ブランチの保護ルールを編集します。
一時的にルール自体を無効化するか、「Allow force pushes(強制プッシュを許可)」 という項目にチェックを入れます。
強制プッシュの実行
保護を外したその瞬間に、すかさずプッシュします。
git push origin main --force
※ 全てのブランチに影響がある場合は –all も付けてください。
保護ルールの復旧(最重要)
プッシュが成功したら、直ちに設定画面に戻り、保護ルールを元に戻してください。
ここを放置すると、今後誰かが誤って歴史を破壊してしまうリスクが残ります。
チームメンバーへの周知徹底
歴史を改変したことには、大きな副作用があります。
リポジトリの全てのコミットIDが変わってしまったため、他のチームメンバーが手元に持っているリポジトリとは整合性が取れなくなります。
もしメンバーが、あなたの修正を知らずに「自分の手元の汚れた歴史」から作業を続け、プッシュしてしまうと、ゾンビのように secret.txt が復活し、歴史が複雑に絡み合って収拾がつかなくなります。
作業完了後、必ず以下の連絡を徹底してください。
【重要連絡】
リポジトリ内の不要ファイル(secret.txt等)削除のため、歴史を書き換えました。
現在お手元にあるリポジトリからは push も pull もしないでください。
既存のフォルダは削除し、新しく git clone し直してください。
これが最も安全で、唯一の解決策です。
まとめ
- 削除ツール: git filter-repo 一択。
- エラー対応: エラーが出たら –force でねじ伏せる。
- リモート反映: 保護設定を一時解除して push –force。
- チーム連携: 全員に clone し直してもらう。
「覆水盆に返らず」と言いますが、Gitの世界では「強力なツールと権限、そしてチームの手間」を代償にすれば、盆に返すことができます。
しかし、そのコストは決して安くありません。