こんにちは。駆け出しエンジニアのtaiyoです。
今回はFast-forwardマージと非Fast-forward(no-ff)マージの違いと、「マージコミット」について勉強したので、まとめた内容を発信したいと思います。
まずマージには、Fast-forwardマージと非Fast-forwardマージがあります。
Fast-forwardマージ(枝がまっすぐな場合)
Fast-forwardマージは、変更に枝分かれの状態がなく、単に一続きの履歴として変更をまっすぐに追加できる場合に適用されます。
例えば、main
ブランチに、前回マージから変更が加えられていないときに、dev
ブランチをそのまま後ろに追加するような形です。
このとき、dev
ブランチに追加の変更を加えた後、main
にマージしようとすると、単純にdev
の変更とコミット履歴がそのままmain
に追加されます。
main: A --- B (前回マージ)
\
dev: C --- D (新しいコミット)
結果 (Fast-forwardマージ):
main: A --- B --- C --- D (すべて直線で追加される)
ちなみに、”Fast-forward” は直訳すると「素早く、前に進んで」という意味で、Fast-forwardマージは、前回の変更の後に続いて、単にまっすぐ変更履歴を追加するだけで、内部処理が素早く終わることからこの名前がついているそうです。
非Fast-forward(no-ff)マージ(枝分かれがある場合)
一方で、2つに枝分かれした変更を統合する(つまり、main
とdev
が異なる履歴を持っている)場合、Fast-forwardをすることはできません。
この場合は、devでの変更とコミット履歴に加えて、マージコミットが作成され、2つの枝を1つにまとめます。
main: A --- B --- E (mainに新しい変更が加わった)
\
dev: C --- D (devで新しい変更)
結果(マージコミット):
main: A --- B --- E --- M (Mはマージコミット)
\ /
C ----- D
マージコミットとは?
「マージコミット」とは、2つ以上のブランチ(分岐した履歴)を統合するために作成される特別なコミットです。
普通のコミットは1つの親コミットを持ち、つまりそのコミットの前にあった1つの状態(親)からの変更を記録しますが、マージコミットは、2つ以上の親コミットを持ちます。つまり、複数の異なる履歴からの変更を統合しているので、どの履歴からマージされたのかを記録します。
また、普通のコミットでは、開発者が自分でコミットメッセージを記述しますが、マージコミットでは、Gitが自動的に「Merge branch 'ブランチ名' into 'ブランチ名'」のようなメッセージを生成します。ただし、手動でコミットメッセージを追加することも可能です。
また、マージコミットは、ファイルの競合を解決した後の状態を記録します。もし2つのブランチで同じファイルに異なる変更があった場合、Gitは自動的に統合できないため、開発者が手動で競合を解消する必要がありますが、その解決結果がマージコミットには含まれます。
変更がないブランチにもマージコミットを記録する方法
先程も書いたように、もしマージ先のブランチに前回のマージから変更が加えられていないなら、マージはデフォルトでFast-forwardマージになります。
git merge dev -m "devをmainにマージします"
そのときはこのように、自分でコメントを残さない限り、特にマージした形跡が残りません。
マージ先のブランチに変更が加えられていないときもマージコミットでしっかりとマージした記録を残したい場合は、Fast-forwardをoffにするために、-—no-ff
コマンドを追加します。
git merge --no-ff dev
これにより、このマージはdev
ブランチのコミット記録がそのままmain
ブランチにも反映されるだけではなく、マージコミットも追加されます(Merge branch 'dev' into 'main’
)。
ご参考になれば幸いです!