git filter-repo で秘密鍵・トークン流出コミットの履歴を完全削除する(強制プッシュと再同期)
要約: git filter-repo を使って .env やシークレット、トークンを Git 履歴から完全に消し去る手順を整理しました。フィルター規則の定義、再書き換えの検証、強制プッシュとチーム全体の再同期、保護ブランチの確認まで一度で把握できます。

概要
秘密鍵は「削除した」で終わりません。
秘密鍵やトークン、.env のような機密情報は、一度コミットされると git rm でファイルを消しても過去のコミットに残ります。
機密情報を正しく整理するには履歴を書き換える必要があります。
最もよく使われるツールが git filter-repo です。
手順
ポイント: 「何を削除するか」を決めて新しい履歴を生成します。
git filter-repo はフィルター規則に従ってコミットを書き換えます。
実務では通常、次の順序で考えます。
⚠️ 重要
- 履歴を書き換える作業です。
- コミット SHA が変わります。
- リモートへ反映するにはたいてい強制プッシュが必要です。
- 既に流出している可能性があるため、履歴整理とは別に必ずキーやトークンを失効させ、再発行してください。
1) 削除対象の定義
- ファイル単位:
.env,id_rsa,secrets.yml - ディレクトリ単位:
config/keys/ - パターン単位:
*.pem,*.p12,*.key
2) 書き換え範囲の決定
- すべてのブランチ・タグを整理するかを決める
- 特定ブランチだけに限定するかを決める
3) 書き換え結果の検証
- 機密内容が履歴のどこにも残っていないか確認する
- ブランチやタグの参照が意図どおりか確認する
4) リモート反映と協力者対応の計画
- 新しいコミットグラフをリモートへ強制プッシュして置き換える
- リモートは旧グラフからハードフォークした状態になり、以前の履歴は基準になりません
- 協力者は旧ツリーを抱えたままでは安全に同期できません
- 原則としてリポジトリを再クローンして新しいツリーへ移行してもらいます
- どうしても既存ローカルを維持する場合は作業ブランチをバックアップし、新しいリモートを基準に再整列します
⚠️ 重要
- リモート更新は
git push --forceが前提です。- 強制プッシュはリモート上の既存コミットを新しいコミットで上書きします。
- 協力者は旧グラフを捨て、新しいコミットを基準に同期し直す必要があります。
- チームが同じタイミングで切り替えないと、pull や merge の状態がねじれたまま残ります。
- GitHub / GitLab ではブランチを保護できます。
- 保護ブランチは強制プッシュを拒否するか、管理者権限と例外設定が必要です。
- 作業前に保護ルールを確認し、必要なら一時的に緩和してください。
例
準備: 作業用クローンから始める
元の作業ディレクトリで直接実行せず、hans-repo のようなミラークローンで進めると安全です。
1git clone --mirror <元のリポジトリ URL> hans-repo.git
2cd hans-repo.git
--mirrorはすべてのブランチとタグ参照を含めて取得します。- 機密情報の削除は、一部ブランチだけでなく全参照を整理するケースが多いです。
1) ファイルを履歴から完全に削除(最も一般的)
例: コミットされた .env をすべて削除。
1git filter-repo --path .env --invert-paths
--path .envで対象パスを指定します。--invert-pathsはそのパスを削除することを意味します。
例: 秘密鍵ファイルを削除。
1git filter-repo --path id_rsa --invert-paths
例: 特定ディレクトリをまとめて削除。
1git filter-repo --path config/keys/ --invert-paths
2) パターンベースで削除したい場合
拡張子で削除したいときは、まず該当ファイルを特定してからパスを指定する方式が安全です。
- 例:
*.pem,*.p12,*.keyをすべて探す - 見つけたパスを基に
--path ... --invert-pathsを実行
パターンを一度に削除しようとして、意図しない証明書やサンプルファイルまで消す事故がよくあります。
3) 検証: 履歴から本当に消えたか
フィルター後は全履歴を対象に痕跡がないか確認します。
1# パスが登場しないか確認
2 git log --all --name-only -- .env
3
4# 出力がなければ履歴から消えています。
5 git log --all --name-only -- config/keys/
4) 新しい履歴をリモートへ強制プッシュし、協力者を再同期させる
整えた履歴を新しいリモートへアップロードするのが最も安全です。
既存リモートを維持する場合は強制プッシュが必要です。
この時点でリモート履歴は旧グラフと完全に分岐します。
1git push --force --all
2git push --force --tags
- 協力者への影響
- 既存ローカルリポジトリは新しいリモートグラフと一致しなくなります
- 単純な
git pullでは整合しません - 推奨はリポジトリを再クローンして新ツリーに同期することです
- 再クローンできない場合は作業ブランチをバックアップし、新しいリモートを基準に rebase や cherry-pick で整列します
まとめ
なぜ filter-repo なのか?
以前は git filter-branch で同様の作業をしていました。
しかし filter-branch は遅く、ミスが起こりやすいため、現在は git filter-repo が事実上の標準的な代替策になっています。
| 項目 | git filter-branch | git filter-repo |
|---|---|---|
| 機密情報の削除 | できるが手間とリスクが大きい | 実務で最もよく使われる用途 |
| パフォーマンス | 遅い | 速い |
| 安全なワークフロー | 後処理が頻繁に必要 | 流れがシンプル |
| 現在の推奨度 | レガシーなので非推奨 | 推奨 |