【Git】間違ってコミットしたファイルを歴史から完全に抹消する方法(git filter-repo)

今回は、Gitリポジトリの「大掃除」についての技術的な解説です。

開発に夢中になっていると、うっかり「入れてはいけないもの」をコミットし、リモートにプッシュしてしまうことがあります。

  • 機密情報: secret.txt、APIキー、.env ファイル。
  • リポジトリの肥大化: テスト用の巨大な動画ファイル、圧縮zip、node_modules。

慌てて git rm で削除してコミットし直しても、それは「削除したという履歴」が追加されただけです。過去のコミットログ(.git ディレクトリの中)には、そのファイルのデータが厳然として残っています。
リポジトリのサイズは減りませんし、履歴を遡れば中身は見えてしまいます。

今回は、Git公式推奨のモダンなツール git-filter-repo を使い、特定のファイルを「歴史上、最初から存在しなかったこと」にする外科手術の手順を解説します。

目次

目標:歴史の改変と強制同期

今回のゴールは以下の通りです。

  1. 完全消去: 過去の全てのコミットから、指定したファイル(今回は例として secret.txt)を物理的に消滅させる。
  2. 歴史の書き換え: ファイル削除に伴い、コミットハッシュ(ID)を再計算して歴史を整形する。
  3. 強制上書き: 既に汚れてしまったリモートリポジトリ(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 -&gt; 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の世界では「強力なツールと権限、そしてチームの手間」を代償にすれば、盆に返すことができます。
しかし、そのコストは決して安くありません。