初めに
普段は Git を使って開発をしていますが、マージする際に「コミットが作成される場合」と「作成されない場合」があり、その違いをあまり理解できていませんでした。この機会に、マージコミットについて整理し、まとめてみたいと思います。
マージ時にコミットが作成される場合とされない場合の違い
ブランチをマージした時、以下のようなコミット履歴が作成されている時、ありますよね。
Merge remote-tracking branch 'origin/testBranch2' into testBranch
このコミットは、マージコミットと呼ばれ、通常のコミットとは少し異なるコミットです。
マージコミットとは
マージコミットとは、複数のコミットをまとめるためのコミットのことです。
詳しく説明していきます。
前提:コミットは親コミットのポインターを持っている
各コミットは、一つ前のコミット(親コミット)のコミットIDを持っています。
以下の図で言うと、B は A のコミット ID を持っており、C は B のコミット ID を持っています。
A --- B --- C
gitツリーのようなグラフは各コミットの親コミットIDを元に作成されます。
A --- B --- C --- D
\
E --- F
※そういう意味では、ブランチとは単なるコミットの連なりをそう呼んでいるだけと言えますね。
このあたりの知識は、以下の記事がとてもわかりやすかったです。
ありがとうございました!
マージコミットには複数の親コミットがある
本題です。
マージコミットとは、複数の親コミットを持つコミットのことです。
例えば、以下のようなブランチがあるとします。
A --- B --- C --- D (main)
\
E --- F (feature)
このfeatureブランチをmainブランチにマージすると、以下のようなグラフになります。
A --- B --- C --- D --- M (main)
\ /
E --- F (feature)
コミットMの親コミットはDとFの二つ。
このように複数のコミットを持つコミットのことをマージコミットと呼びます。
マージコミットが発生する条件
前述の通り、マージ時に必ずマージコミットが発生するというわけではありません。
マージに大きく分けて二種類の方法がある
マージには大きく分けて二つの方法があります。
Fast-forwardとNon-Fast-forwardの二種類です。
どちらのマージ方法が選択されるかはgit merge
コマンド実行時に内部で判断されます。
Fast-forward(マージコミット発生しない)
マージコミットが作成されないシンプルなマージ方法です。
以下の条件で選択されます。
- マージ先ブランチの先端がマージ元ブランチの先祖コミットに当たる
- マージ元ブランチが切られてから、マージ先ブランチに変更がない
以下のようなイメージ。
A --- B (main)
\
E --- F (feature)
マージすると以下のようになります。
形は変わっていますが、実際には main
ブランチの先端が B から F に移動しただけです。
A --- B --- E --- F (main, feature)
Non-Fast-forward(マージコミット発生する)
マージコミットが作成されるマージ方法です。
Fast-forwardの条件に当てはまらない場合に選択されます。
以下のようなイメージ
A --- B --- C --- D (main)
\
E --- F (feature)
このfeatureブランチをmainブランチにマージすると、以下のようなグラフになり、マージコミットMが作成されます。
A --- B --- C --- D --- M (main)
\ /
E --- F (feature)
Fast-forward マージが適用される場合でも、--no-ff オプションを使用することで、意図的にマージコミットを作成することができます。
git merge --no-ff featureBranch
Fast-forwardマージ周りについては以下の記事が分かりやすかったです。
ありがとうございました!
終わりに
マージコミットが発生しないのはシンプルでよいですが、マージ履歴を残すという意味ではマージコミットがあるのはメリットとして大きそうですね。使い分けが大事だと思いました。
参考