かなり長い間 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