git rebase でコンフリクトしたコミットが何かを調べる


2015年 05月 01日

問題

git rebase は便利ですが、時々コンフリクトが起きて停止します:

$ git rebase master
Applying: Add GeneratedColumn
Applying: Add EnumerateGeneratedColumns
Applying: Use EnumerateGeneratedColumns to CreateQuery
Using index info to reconstruct a base tree...
M       AAA/BBB/Summary.cs
Falling back to patching base and 3-way merge...
Auto-merging AAA/BBB/Summary.cs
CONFLICT (content): Merge conflict in AAA/BBB/Summary.cs
Failed to merge in the changes.
Patch failed at 0037 Use EnumerateGeneratedColumns to CreateQuery
The copy of the patch that failed is found in:
   c:/Users/VimWizard/projects/SecretProject/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

まず何が影響してるか、 git statusgit diff で確認しますよね。
あれこれ端末内で情報を表示して状況を確認した結果、コンフリクトの解決に取り掛かる訳ですが、
解決するには元々 何のコミットをapplyしようとしてコンフリクトしたのか という情報も重要です。
この情報自体は上記の git rebase からのメッセージに含まれています。
が、しかし、

  • git statusgit diff を何回も実行しているので git rebase のメッセージは流れています。
    あのメッセージがどこにあったのかスクロールして探すのは面倒です。
  • 仮に端末内の出力を検索したとしても、
    git rebase はコンフリクトしたコミットのコミットメッセージは表示してくれているものの、
    コミットIDは表示してくれないので、結局問題のコミットにおける変更点だけを見るには不向きです。

どうにかして一発でapplyし損ねたコミットを表示できないものでしょうか。

解答

ヒントは git rebase からのメッセージにあります:

The copy of the patch that failed is found in:
   c:/Users/VimWizard/projects/SecretProject/.git/rebase-apply/patch

このファイルには以下の内容が含まれています:

  • 1行目: applyし損ねたコミットのID
  • 2行目以降: そのコミットの変更内容(git diff の形式)

という訳で問題のコミットは次のコマンドで即座に表示できます:

git show $(head -n1 "$(git rev-parse --git-dir)/rebase-apply/patch")

でもこれだとrebase中でない時にうっかり実行するとよく分からないことになりますね。
ファイルが存在する場合にのみ実行されるようにしましょう:

f="$(git rev-parse --git-dir)/rebase-apply/patch"
if [ -f "$f" ]
then
  git show $(head -n1 "$f")
else
  echo "Heh?"
fi

流石に長ったらしくて即座に入力できないし覚えてもいられないので好みに合わせてエイリアスを設定しておくと良いでしょう:

git config alias.wtf 'f="$(git rev-parse --git-dir)/rebase-apply/patch"; if [ -f "$f" ]; then git show $(head -n1 "$f"); else echo "Heh?"; fi'

これで困った時は

git wtf

で解決できるようになりました。やりましたね。

次回予告

次回は Mercurial 編です。