Git ブランチ | git merge
git merge コマンド
マージは、Git で分岐した内容を再び統合する方法である。git merge は、git branch コマンドで作成された独立した複数の開発ラインを、1 つのブランチへ統合するコマンドである。
ここで説明するすべてのコマンドは、現在のブランチにマージする点に注意してほしい。現在のブランチはマージ結果で更新されるが、対象ブランチ、つまり引数で指定したブランチはそのまま残る。そのため git merge は通常、マージ先のブランチを選択する git checkout と、不要になった対象ブランチを削除する git branch -d と一緒に使われる。
使い方
git merge <branch>
指定したブランチを現在のブランチへマージするコマンドである。Git はマージアルゴリズムを自動的に選択する。
git merge --no-ff <branch>
指定したブランチを現在のブランチへマージするが、そのとき常に、たとえ fast-forward が可能であっても、マージコミットを作成してマージする。このコマンドは、リポジトリで発生したすべてのマージを記録したい場合に便利である。
補足説明
独立したブランチで機能開発が完了したら、それをメインコードベースへ反映する必要がある。Git には、リポジトリ履歴の形に応じて複数のアルゴリズム、つまり fast-forward マージと three-way マージが用意されている。
fast-forward マージは、現在のブランチの先端から対象ブランチへ向かって 1 本の直線的な経路だけがある場合に適用される。この場合、実際の意味でのマージは行われず、現在のブランチの先端を対象ブランチの先端へ移動するだけで統合される。これにより、対象ブランチから到達できたコミットが、両方のブランチから到達できるようになる。
Git チュートリアル: fast-forward マージ
しかし、ブランチが分岐している場合は fast-forward マージを適用できない。対象ブランチへ向かう経路が 1 本だけではないため、履歴を結合するには three-way マージを使う必要がある。three-way マージでは、2 つの履歴を結合するための専用コミットが作成される。この名前は、2 つのブランチの先端コミットと共通祖先コミットという 3 つのコミットを使ってマージコミットを作成することに由来する。
Git チュートリアル: three-way マージ
どちらのマージアルゴリズムも利用できるが、多くの開発者は小さな機能やバグ修正には、しばしば rebase 後の fast-forward マージを使い、長期にわたる機能には three-way マージを使う傾向がある。後者の場合、生成されたマージコミットは 2 つのブランチの象徴的な結合として機能する。
コンフリクトの解決
マージしようとしている 2 つのブランチで、同じファイルの同じ部分に変更がある場合、Git はどちらのバージョンを使うべきか判断できない。そのような状況になると、マージコミットを作成する前に停止し、手動でコンフリクトを解決するよう求める。
Git のマージプロセスが優れている点は、マージ時のコンフリクト解決にも通常の編集、ステージ、コミットのワークフローを使えることである。マージ時にコンフリクトが発生した場合、git status を実行すると解決が必要なファイルが表示される。たとえば 2 つのブランチで hello.py の同じ部分が変更されていると、次のような情報が表示される。
# On branch master
# Unmerged paths:
# (use "git add/rm ..." as appropriate to mark resolution)
#
# both modified: hello.py
#
ここでマージする内容を確認し、必要な修正を行う。修正が終わってマージを続ける準備ができたら、git add でコンフリクトが発生したファイルをステージング領域に追加する。続いて通常の git commit でマージコミットを作成する。この過程は通常のスナップショットコミットと完全に同じであるため、一般的な開発者にとってもマージ作業は理解しやすい。
また、マージ時のコンフリクトは three-way マージの場合にだけ起こることを覚えておこう。fast-forward マージで変更がコンフリクトを起こすことはない。
git merge の使用例
fast-forward マージ
まず fast-forward マージの例を示す。次のコードでは、新しいブランチを作成して 2 つのコミットを追加し、最後に fast-forward マージでそれを master ブランチへ統合している。
# Start a new feature
git checkout -b new-feature master
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Merge in the new-feature branch
git checkout master
git merge new-feature
git branch -d new-feature
これは、長期的な機能開発を行う大規模組織向けのブランチではなく、独立した小規模開発のために短期間の機能ブランチを使う場合の一般的な流れである。
また、new-feature ブランチは master ブランチから到達可能であるため、git branch -d を使っても警告は表示されない。
three-way マージ
次の例は前の例と似ているが、機能開発が進んでいる間に master も進んでいるため、three-way マージが必要になる。このシナリオは、大規模な機能の場合や、プロジェクトで複数の開発者が同時に作業している場合によく発生する。
# Start a new feature
git checkout -b new-feature master
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Develop the master branch
git checkout master
# Edit some files
git add <file>
git commit -m "Make some super-stable changes to master"
# Merge in the new-feature branch
git merge new-feature
git branch -d new-feature
この場合、履歴の形を崩さずに master を new-feature へ移動できないため、fast-forward マージは実行できない。
一般的なワークフローでは、new-feature が実際には長期間の開発を必要とする大規模機能であり、その間に master へ新しいコミットが追加されることが多い。機能ブランチが上の例のように小規模である場合は、それを master に rebase してから fast-forward マージする方がよいこともある。この方法は不要なマージコミットを減らし、プロジェクト履歴の複雑化を防ぐ。