はじめに
Gitのコミットには「Author(著者)」と「Committer(コミッター)」という2つのユーザー情報が記録されています。設定ミスや環境変更により、これらを後から修正したい場面があります。
この記事では、git filter-branch を使った書き換え方法と、後処理について解説します。
AuthorとCommitterの違い
| 項目 | 説明 |
|---|---|
| Author | 変更内容を最初に作成した人 |
| Committer | そのコミットを実際にリポジトリに適用した人 |
通常の作業では両者は同一ですが、git am やチェリーピックなど、他人の変更を取り込む場合に異なることがあります。
Author/Committerの確認方法
git log --format='%h %s | Author: %an <%ae> | Committer: %cn <%ce>'
実行結果例:
a1b2c3d 機能Aを追加 | Author: yamada <yamada@example.com> | Committer: yamada <yamada@example.com>
e4f5g6h バグ修正 | Author: tanaka <tanaka@example.com> | Committer: yamada <yamada@example.com>
最新N件だけ確認したい場合は -n オプションを使用:
git log -n 5 --format='%h %s | Author: %an <%ae> | Committer: %cn <%ce>'
何をしているか:
-
-n 5:最新5件のみ表示 -
%h:コミットハッシュ(短縮形) -
%s:コミットメッセージ(件名) -
%an/%ae:Author の名前・メールアドレス -
%cn/%ce:Committer の名前・メールアドレス
書き換え手順
1. 全コミットのAuthor/Committerを書き換える
git filter-branch -f --env-filter '
GIT_AUTHOR_NAME="新しい名前"
GIT_AUTHOR_EMAIL="new@example.com"
GIT_COMMITTER_NAME="新しい名前"
GIT_COMMITTER_EMAIL="new@example.com"
' HEAD
何をしているか:
-
filter-branchはコミット履歴を走査して、各コミットに対してフィルタ処理を適用するコマンド -
--env-filterは環境変数を書き換えるフィルタ。Gitはコミット時にGIT_AUTHOR_*/GIT_COMMITTER_*環境変数を参照するため、これを上書きすることで情報を変更できる -
-fは既存のバックアップを上書きして強制実行するオプション
2. 特定のメールアドレスのみ書き換える
git filter-branch -f --env-filter '
if [ "$GIT_AUTHOR_EMAIL" = "old@example.com" ]; then
GIT_AUTHOR_NAME="新しい名前"
GIT_AUTHOR_EMAIL="new@example.com"
fi
if [ "$GIT_COMMITTER_EMAIL" = "old@example.com" ]; then
GIT_COMMITTER_NAME="新しい名前"
GIT_COMMITTER_EMAIL="new@example.com"
fi
' HEAD
何をしているか:
- 条件分岐で、変更対象のメールアドレスに一致するコミットのみを書き換える
- 複数人で作業しているリポジトリで、自分のコミットだけを修正したい場合に使用
3. リモートへの反映(必要に応じて)
git push --force-with-lease origin main
何をしているか:
-
--force-with-leaseはリモートが想定通りの状態か確認してから強制pushする - 他の人が先にpushしていた場合は失敗するため、
--forceより安全
注意: 履歴を書き換えるとコミットハッシュが全て変わります。共有リポジトリで実行する場合は、他のメンバーとの調整が必要です。
refs/original/ の削除
filter-branch 実行後、元の履歴がローカルの refs/original/ にバックアップとして残ります。
確認方法
git for-each-ref refs/original/
削除方法
git for-each-ref --format="delete %(refname)" refs/original/ | git update-ref --stdin
何をしているか:
-
for-each-refでrefs/original/配下の全参照を列挙 -
--format="delete %(refname)"で削除コマンド形式に整形 -
update-ref --stdinにパイプして一括削除
代替方法(シンプル版)
rm -rf .git/refs/original/
直接ファイルを削除する方法。参照が実際にファイルとして存在する場合はこれでも可。
ガベージコレクション(任意)
git reflog expire --expire=now --all
git gc --prune=now
何をしているか:
-
reflog expireで参照ログを即時期限切れにする -
gc --prune=nowで不要なオブジェクトを即時削除し、リポジトリサイズを縮小
補足:git filter-repo について
filter-branch は公式に非推奨となっており、代替として git filter-repo が推奨されています。ただし、filter-repo は別途インストールが必要なため、簡易な書き換えには filter-branch も依然として実用的です。
まとめ
| やりたいこと | コマンド |
|---|---|
| Author/Committer書き換え | git filter-branch --env-filter |
| バックアップ参照の削除 |
git update-ref または rm -rf
|
| 不要オブジェクトの削除 | git gc --prune=now |
履歴の書き換えは強力な操作です。実行前にバックアップを取ることをお勧めします。