はじめに
Gitを使っていて「あ、パスワードをコミットしてしまった!」「過去のコミットの著者名を全部変更したい」といった経験はありませんか?
今回は、そんな時に役立つgit filter-branch
という強力なコマンドについて解説します。このコマンドは「Gitの歴史改変ツール」とも呼べる、知る人ぞ知る上級コマンドです。
git filter-branchとは?
git filter-branch
は、Gitリポジトリの履歴を書き換えるためのコマンドです。単一のコミットだけでなく、全履歴を対象に一括で変更を適用できるのが特徴です。
基本構文
1
| git filter-branch [--option] '<command>' [revision-range]
|
主なオプション:
--tree-filter
: 各コミットのファイルツリーを直接編集--index-filter
: インデックス(ステージング)を編集--env-filter
: 環境変数(著者情報など)を編集--msg-filter
: コミットメッセージを編集
実践例1:機密情報の削除
最も一般的な使用例は、誤ってコミットしてしまった機密情報の削除です。
パスワードファイルを全履歴から削除
1
2
3
4
| # passwords.txtを全履歴から削除
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch passwords.txt' \
--prune-empty --tag-name-filter cat -- --all
|
.envファイルを削除(より安全な方法)
1
2
3
4
5
6
7
8
9
10
| # バックアップを作成
git branch backup-before-cleanup
# .envファイルを全履歴から削除
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch .env' \
--prune-empty --tag-name-filter cat -- --all
# 削除を確認
git log --all --full-history -- .env
|
実践例2:著者情報の変更
GitHubのユーザー名を変更した場合など、過去のコミットの著者名を変更したい場合があります。
特定の著者名を一括変更
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #!/bin/bash
git filter-branch --env-filter '
OLD_EMAIL="old@example.com"
CORRECT_NAME="新しい名前"
CORRECT_EMAIL="new@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
|
実際の使用例:tmy-ss-310 → tmy-310
私が実際に使用したケースです。GitHubのユーザー名を変更したため、過去のコミット履歴も更新しました:
1
2
3
4
5
6
7
8
9
10
| git filter-branch -f --env-filter '
if [ "$GIT_AUTHOR_NAME" = "tmy-ss-310" ]; then
export GIT_AUTHOR_NAME="tmy-310"
export GIT_AUTHOR_EMAIL="mito.motohiro@gmail.com"
fi
if [ "$GIT_COMMITTER_NAME" = "tmy-ss-310" ]; then
export GIT_COMMITTER_NAME="tmy-310"
export GIT_COMMITTER_EMAIL="mito.motohiro@gmail.com"
fi
' -- --all
|
実践例3:ディレクトリ構造の変更
サブディレクトリを新しいリポジトリとして切り出す
1
2
3
4
| # frontendディレクトリだけを抽出して新しいリポジトリに
git filter-branch --subdirectory-filter frontend HEAD
# これにより、frontendディレクトリがルートディレクトリになる
|
全ファイルをサブディレクトリに移動
1
2
3
4
5
| git filter-branch --index-filter \
'git ls-files -s | sed "s-\t\"*-&newsubdir/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
|
実践例4:大きなファイルの削除
リポジトリサイズを削減するため、大きなファイルを履歴から削除します。
1
2
3
4
5
6
7
8
9
10
11
| # 100MB以上のファイルを検出
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
sed -n 's/^blob //p' |
sort --numeric-sort --key=2 |
tail -10
# 大きなファイル(例:huge-video.mp4)を削除
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch huge-video.mp4' \
--prune-empty --tag-name-filter cat -- --all
|
filter-branchの注意点
⚠️ 破壊的操作
git filter-branch
は履歴を完全に書き換えるため:
- 必ずバックアップを作成
1
| git branch backup-$(date +%Y%m%d)
|
- 強制プッシュが必要
1
| git push --force origin main
|
- チームメンバーへの影響
- 他の開発者は再クローンが必要
- 進行中の作業がある場合は要調整
パフォーマンスの問題
大規模なリポジトリでは処理に時間がかかります:
- 1000コミット: 数分
- 10000コミット: 30分以上
代替ツール:git filter-repo
Git 2.24以降では、git filter-repo
が推奨されています:
1
2
3
4
5
6
7
8
9
10
| # インストール
pip install git-filter-repo
# 使用例:メールアドレスの変更
git filter-repo --email-callback '
return email.replace(b"old@example.com", b"new@example.com")
'
# パスの削除
git filter-repo --path passwords.txt --invert-paths
|
filter-repoの利点
- 高速: filter-branchの10-720倍高速
- 安全: デフォルトで新しいリポジトリに出力
- 使いやすい: より直感的なオプション
実用的な使い分け
git filter-branchを使うべき場合
- Gitの標準機能のみで完結したい
- 簡単な著者名変更や単一ファイル削除
- 追加ツールのインストールが困難な環境
git rebaseやresetで十分な場合
- 最近のコミット(10個程度)の修正
- 個人プロジェクトで履歴の詳細が不要
- クリーンな履歴を優先
git filter-repoを使うべき場合
- 大規模なリポジトリ
- 複雑なフィルタリング条件
- パフォーマンスが重要
トラブルシューティング
エラー: “Cannot create a new backup”
1
2
| # 既存のバックアップを削除
rm -rf .git/refs/original/
|
リモートとの同期問題
1
2
3
4
5
| # ローカルの変更を強制的に反映
git push --force-with-lease origin main
# より安全:新しいブランチにプッシュ
git push origin main:main-filtered
|
容量が減らない場合
1
2
3
| # ガベージコレクションを実行
git reflog expire --expire=now --all
git gc --prune=now --aggressive
|
まとめ
git filter-branch
は強力なツールですが、使用には注意が必要です:
✅ 使うべき時
- 機密情報の緊急削除
- 著者情報の一括修正
- リポジトリの大規模なリファクタリング
❌ 避けるべき時
- 公開済みの大規模プロジェクト
- チーム開発中のアクティブなリポジトリ
- 簡単な修正で済む場合
適切に使えば、Gitの履歴をクリーンに保ち、セキュリティ問題を解決できる強力な味方になります。ただし、「大いなる力には大いなる責任が伴う」ことを忘れずに!
参考リンク
この記事が役に立ったら、ぜひシェアしてください!Gitの困った時の救世主として、多くの開発者の助けになれば幸いです。