かなり長い間 git svn を使わざるを得なかった愛用していたこともあって、個人的に git log --first-parent が大好きなんだけど、ふとググったらHamanoさん自らが素晴らしいエントリを書いてたので紹介。
Depending on the work style of their project, sometimes people wonder what story git log --first-parent output is trying to tell, and this article is about demystifying it.
要は git log --first-parent するとトピックブランチでの細かい修正をサマリ的に表示して、プロジェクトの変更の流れを俯瞰できて良いって話なのだけど、この仕組みを機能させたければ、mergeの時にfirst parentを意識したマージを行う必要がある。
以下は--first-parentが機能する例。
【コミットを作成】 % git init Initialized empty Git repository in sample1/.git/ % echo "Initial line" > initial.txt % git add initial.txt; git commit -am"initial.txt" [master (root-commit) fc1e970] initial.txt 1 file changed, 1 insertion(+) create mode 100644 initial.txt 【トピックに切り替え、2回コミット】 % git checkout -b topic Switched to a new branch 'topic' % echo "{ start topic branch ..." >> initial.txt % git commit -am"Start implementing topic" [topic fd2941e] Start implementing topic 1 file changed, 1 insertion(+) % echo "... the end of topic branch }" >> initial.txt % git commit -am"Finish implementing topic" [topic edf34c4] Finish implementing topic 1 file changed, 1 insertion(+) 【マスタに戻り、1つコミットを追加】 % git checkout master Switched to branch 'master' % echo "Second file" > second.txt % git add second.txt; git commit -am"Add second.txt" [master d6e1162] Add second.txt 1 file changed, 1 insertion(+) create mode 100644 second.txt 【マスタからトピックをマージ】 % git merge --no-ff topic Merge made by the 'recursive' strategy. initial.txt | 2 ++ 1 file changed, 2 insertions(+) 【トピックでの作業が、1コミットとして表示される】 % git log --first-parent commit 0f623aeca3a446e4ddf7bdb1a1174ffd8d2b7186 Merge: d6e1162 edf34c4 Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 19:05:32 2012 +0900 Merge branch 'topic' commit d6e11624e9586d39b4149ec0f98c0904ac10bd12 Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 19:05:22 2012 +0900 Add second.txt commit fc1e97092e40f0714f3d3f2060dfa7b00eda0517 Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 18:54:11 2012 +0900 initial.txt
ところが、マスタに戻らずにトピックからmergeコマンドを叩くと、--first-parentの恩恵が受けられなくなる。
【トピックからマスタをマージ】 % git branch master * topic % git merge --no-ff master Merge made by the 'recursive' strategy. second.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 second.txt 【マスタへ移り、マージ済のトピックへfast-forward】 % git checkout master Switched to branch 'master' % git merge --ff-only topic Updating d6e1162..5f7da9d Fast-forward initial.txt | 2 ++ 1 file changed, 2 insertions(+) 【マスタの履歴が隠され、トピックの履歴が公に出てしまっている】 % git log --first-parent commit 5f7da9dc1f23b421a2fdcac7ad44d59c83af4287 Merge: edf34c4 d6e1162 Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 19:08:59 2012 +0900 Merge branch 'master' into topic commit edf34c4de114fa1af842e80655de698aaec8baa4 Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 18:56:16 2012 +0900 Finish implementing topic commit fd2941e029da767d79924c1020ef800583405afe Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 18:55:48 2012 +0900 Start implementing topic commit fc1e97092e40f0714f3d3f2060dfa7b00eda0517 Author: Masahiro Honma <hir...@cpan.org> Date: Wed Nov 7 18:54:11 2012 +0900 initial.txt
基本的には、「mergeを叩くときはmaster(により近い)ブランチから」を心がければ大丈夫。だけど、Hamanoさんのエントリでも最後に取り上げられているように、pullをするときは注意。masterとorigin/masterでは後者の方がmasterに近いと考えられるため、master側にマージコミットができるpull操作は--first-parentの恩恵を壊してしまう。
この件に関するHamanoさんの見解は以下に引用する通り。
It is a judgement call if this "purist" approach to avoid the "last minite" first-parent breakage is worth it. I personally do not think it is, but others may disagree.
自分は考え方は"purist"に近いけど、このパターンだと状況によっては(例えば、愚直に切り戻すとテストが大変とかデグレのリスクが大きいとき)次のようにするかも。
【HEADはマスタという前提。masterを別名に退避】 $ git branch conflicted-master master 【masterをorigin/masterの状態にする】 $ git fetch origin ← pullの代わり(普段からpull使わない派) $ git reset --hard origin/master 【masterへpushできなかったmaster(M)を再度マージ】 $ git merge conflicted-master 【「conflicted-master」って名前を表に出したくなければコミットを編集】 $ git commit --amend --編集-- 【テストし、masterをpush。競合したら最初に戻る・・・】 $ test $ git push