git filter-branchでコミットを一括変換する

git filter-branchでコミットのメッセージやメールアドレス・名前の変更する方法をまとめました。


1 git filter-branchのフォーマット

$ git filter-branch [オプション] [コマンド] [リビジョン]

[コマンド]はシングルコーテーションで囲って複数のコマンド群にしても動作します。ただし、既存のシェルスクリプトのようには使えないようです(evalコマンド相当の処理のみ?)。複雑な処理はシェルスクリプト等に記述し、そのシェルを[コマンド]で呼ぶようにすると良いでしょう。

[リビジョン]は変換対象となるリビジョンを指定します。HEAD~10..HEAD等のgit diffの指定方法と同様の方法です。[リビジョン]を指定しない場合は全てのリビジョンが指定されたことになります。

2 特定の変更を加えた後に変更を加える場合のエラー

以下のエラーメッセージが表示されます。

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f

git filter-branchに-fを追加するか、.git/refs/originalを削除することでエラーを回避できます。

3 注意点

サーバのリポジトリのバックアップを取ってから実行してください。変換はクローンしたディレクトリで実行し、最後にリポジトリへ反映させます。コミット番号(ハッシュ値)は全て変更されます。

4 既存コミットの参照

 

標準入力(–msg-filter) 既存コミットログ(catやsed)
標準出力(–msg-filter) 新規コミットログ(echo)
${GIT_COMMIT} コミット番号を取得できます
${GIT_AUTHOR_EMAIL} Authorのemailを参照できます
${GIT_AUTHOR_NAME} Authorの名前を参照できます

 

5 本ページで使用するリポジトリ

$ git log
commit 402f8d9d394f10c410703d1beb9e2e86d6d665a7
Author: hiroom2 <hiroom2@debian-8>
Date:   Thu Jul 9 16:39:05 2015 +0900

    Add new file with multiple line commig message.
    Hello,
    World.

commit adcfbc87153ca372d85d8bde64c75b6551283df7
Author: hiroom2 <hiroom2@debian-8>
Date:   Thu Jul 9 16:38:41 2015 +0900

    Initial commit

6 コミットログに特定文字列を一括して追加する

6.1 Git-Hooksに対応するためコミット番号に空チケット番号を設定する

Git-Hooksはコミットログに"refs #[チケット番号]"がないコミットを受理しないようにするスクリプトです。既存のリポジトリにGit-Hooksを導入する場合、新規のコミットは全て上記のチケット番号への参照が含まれている必要があります。特にブランチをリポジトリにpushする場合は既存のコミットもチケット番号への参照が含まれている必要があります。

そこで以下の2つの方法でコミットログを変換します。

6.2 先頭行にチケット番号を追加する

既存のコミットログの先頭に空のチケット番号を追加することで対応します。

git filter-branch --msg-filter 'echo "refs #0. "; cat'

先頭行に"refs #0"が追加されました。

$ git log
commit 8fcc8e20b1c8d64e29e158c2b8b65c8301f94c43
Author: hiroom2 <hiroom2@debian-8>
Date:   Thu Jul 9 16:39:05 2015 +0900

    refs #0.
    Add new file with multiple line commig message.
    Hello,
    World.

commit 4ea93be5881b6aeae2147c811597955e1ccc90ed
Author: hiroom2 <hiroom2@debian-8>
Date:   Thu Jul 9 16:38:41 2015 +0900

    refs #0.
    Initial commit

6.3 先頭行の末尾にチケット番号を追加する

–msg-filterオプションのコマンドで任意のプログラムを呼ぶこともできます。かなり不格好ではありますが、以下のスクリプトは与えられた文字列の先頭行の末尾に"refs #0"を追加して文字列を出力します。

$ cat ~/bin/insert_ticket_number.sh
#!/bin/sh

tmpfile=`mktemp`
echo "$1" > ${tmpfile}
top=1
while read line; do
  if [ ${top} -eq 1 ]; then
    echo "${line} refs #0"
    top=0
  else
    echo "${line}"
  fi
done < ${tmpfile}
rm -f ${tmpfile}

–msg-filterオプションのコマンドでシェルスクリプトを呼び出します。

$ git filter-branch --msg-filter \
'msg=`cat`; insert_ticket_number.sh "${msg}"'

先頭行に"refs #0"が追加されました。

$ git log
commit 596deba403b581fd6d88d66ce9e13d55e15882eb
Author: hiroom2 <hiroom2@debian-8>
Date:   Thu Jul 9 16:39:05 2015 +0900

    Add new file with multiple line commig message. refs #0
    Hello,
    World.

commit 7be7e05cd61292ec7e183c5e67897d4e1b758620
Author: hiroom2 <hiroom2@debian-8>
Date:   Thu Jul 9 16:38:41 2015 +0900

    Initial commit refs #0

7 コミットログの特定文字列を置き換える

コマンドにsedを用いることで特定文字列を変更できます。本ページで利用しているリポジトリのコミットログにcommigというタイポがあるのでcommitに修正します。

$ git filter-branch --msg-filter 'sed -e "s/commig/commit/g"'
$ git log
commit b8e95a7a17c7c877b982100df9ddbafe258ca976
Author: hiroom2 <hiroom2@debian-8.com>
Date:   Thu Jul 9 16:39:05 2015 +0900

    Add new file with multiple line commit message.
    Hello,
    World.

commit adcfbc87153ca372d85d8bde64c75b6551283df7
Author: hiroom2 <hiroom2@debian-8.com>
Date:   Thu Jul 9 16:38:41 2015 +0900

    Initial commit

8 メールアドレスを変更する

メールアドレスを一括して変更します。既存のメールアドレスを${GIT_AUTHOR_EMAIL}で参照し、GIT_AUTHOR_EMAILを上書きした後にexportすることで、新規のメールアドレスになります。

$ git filter-branch --env-filter '
if [ ${GIT_AUTHOR_EMAIL} = hiroom2@debian-8 ]; then
  export GIT_AUTHOR_EMAIL=hiroom2@newmail.com
fi
'
$ git log
commit de3638a4bf21e2a215df44c033177a419684dc83
Author: hiroom2 <hiroom2@newmail.com>
Date:   Thu Jul 9 16:39:05 2015 +0900

    Add new file with multiple line commit message. refs #0
    Hello,
    World.

commit 91d307203876e1cb36f4e940a60f817d425e5b8a
Author: hiroom2 <hiroom2@newmail.com>
Date:   Thu Jul 9 16:38:41 2015 +0900

    Initial commit refs #0

9 名前を変更する

名前を一括して変更します。既存の名前を${GIT_AUTHOR_NAME}で参照し、GIT_AUTHOR_NAMEを上書きした後にexportすることで、新規の名前になります。

$ git filter-branch --env-filter '
if [ ${GIT_AUTHOR_NAME} = hiroom2 ]; then
  export GIT_AUTHOR_NAME=hiroom3
fi
'
$ git log
commit 694dc7c8161f5335b7319cc06b881e6b046e7794
Author: hiroom3 <hiroom2@newmail.com>
Date:   Thu Jul 9 16:39:05 2015 +0900

    Add new file with multiple line commit message. refs #0
    Hello,
    World.

commit 4b53240b29fbf395e3a69fa0eafdecce8c00d2c7
Author: hiroom3 <hiroom2@newmail.com>
Date:   Thu Jul 9 16:38:41 2015 +0900

    Initial commit refs #0

10 特定のコミット番号のみ変更する

${GIT_COMMIT}を参照するとコミット番号が分かります。そこで${GIT_COMMIT}を判定して特定のコミット番号のみ変換するようにします。

$ git filter-branch --msg-filter '
if [ ${GIT_COMMIT} = 694dc7c8161f5335b7319cc06b881e6b046e7794 ]; then
  echo "Add new file with multiple line commit message. refs #0"
  echo "Hello, World"
else
  cat
fi
'

特定のコミット番号以外はcatで既存コミットログをそのまま新規コミットログにしている点に注意してください。

$ git log
commit 23f6e53fe21c78a5654a2d4482f334f7528787fc
Author: hiroom3 <hiroom2@newmail.com>
Date:   Thu Jul 9 16:39:05 2015 +0900

    Add new file with multiple line commit message. refs #0
    Hello, World

commit 4b53240b29fbf395e3a69fa0eafdecce8c00d2c7
Author: hiroom3 <hiroom2@newmail.com>
Date:   Thu Jul 9 16:38:41 2015 +0900

    Initial commit refs #0

11 リポジトリへ反映

強制的にリポジトリの内容を上書きします。

$ git push -f