3ウェイマージ
ブランチとブランチをマージする際、各ブランチのコミット状況によっては、ファーストフォワードによるマージを行うことができません。このようなマージは、「3ウェイマージ」で行います。
ファーストフォワードできない状況
「ファーストフォワード」によるマージができない状況です。以下のように「master」ブランチと「Ver2」ブランチがあり、「master」ブランチに「Ver2」ブランチをマージしようとしています。
この時、「master」ブランチのブランチヘッドを単純に移動する「ファーストフォワード」によるマージは行えません。
そもそもブランチヘッドの移動先がありません。
これは、「master」ブランチと「Ver2」ブランチが「Commit3」で分岐しており、分岐後(ブランチ後)それぞれ異なる変更内容をコミットしているためです。
ファーストフォワードは利用できない
もし無理やり「master」ブランチのブランチヘッドを「Ver2」ブランチに持って行ってしまうと、「Commit5」の変更内容が失われてしまいます。逆も然りで、「Ver2」ブランチのブランチヘッドを「master」ブランチに持って行ってしまうと、「Commit4」と「Commit6」の変更内容が失われてしまいます。
従って、単純なブランチヘッドの移動でマージを行う「ファーストフォワード」が利用できない、ということになります。
3ウェイマージ(3-Way Merge)
このような状況でマージを行うと、「Git」は3種類の「スナップショット」を使用して「3ウェイマージ」を行います。マージコミット(Merge Commit)
「3ウェイマージ」を行うと、マージ結果を自動的にコミットします。従って、新たに「スナップショット」が自動的に生成されます。
このコミットのことを、「マージコミット」と表現します。
「マージコミット」の「コミットオブジェクト」は、複数の「親コミットオブジェクト」を持ちます。
マージコンフリクト(Merge Conflict)
変更内容によっては、変更内容が衝突する場合があります。例えば、AさんとBさんが同じファイルの同じ行を修正したとします。
この時、どちらの修正内容を適用(採用)すればいいか、「Git」は判断できません。
この状況を、「マージコンフリクト」といいます。
「マージコンフリクト」が発生した場合、「Git」はマージを中断し、ユーザーに判断を委ねます。
ユーザーは、衝突した変更内容を確認してファイルを修正し、自分でコミットを行います。
「マージコンフリクト」が発生しなかったファイルは、マージが完了している状態になります。
3ウェイマージによるマージ例
「3ウェイマージ」によるマージ例です。1.ブランチの切り替え
「master」ブランチに「Ver2」ブランチをマージするため、まず「master」ブランチを作業対象に切り替えます。2.マージを行う
「master」ブランチに「Ver2」ブランチをマージします。ここから先は、「Git」がマージ処理を行います。
「Git」はまず、「master」ブランチと「Ver2」ブランチの共通の「コミットオブジェクト」を探します。
共通の「コミットオブジェクト」とは、分岐直前のコミットオブジェクトのことです。
「master」ブランチと「Ver2」ブランチの共通の「コミットオブジェクト」は、「Commit3」であることがわかります。
3.スナップショットを使用してマージを行う
「Git」は以下の3種類の「スナップショット」を使用してマージを行います。- ブランチ共通の「Commit3」のスナップショット
- 「master」ブランチのブランチヘッドが指す「Commit5」のスナップショット
- 「Ver2」ブランチのブランチヘッドが指す「Commit6」のスナップショット
4.マージ完了
マージが完了するとコミットが自動的に行われ、「Commit7」が自動的に生成されます。「Commit7」のように、マージにより作成されたコミットを、「マージコミット」と表現します。
これで「Ver2」ブランチの変更内容が、「master」ブランチにマージされました。
「Commit7」の「親コミットオブジェクト」は、「Commit5」と「Commit6」になります。
「Ver2」ブランチがもう不要なら、「Ver2」ブランチを削除するとよいでしょう。
マージの中止とマージコンフリクト
「3ウェイマージ」は万能ではありません。もし上記の「4.スナップショットを使用してマージを行う」の段階で、単純なマージができないケースが存在した場合、マージは行われません。
これを「マージコンフリクト(Merge Conflict)」といいます。
コンフリクトは、衝突や矛盾という意味です。
どちらの変更内容を優先すればよいのか分からない
例えば、「Commit5」と「Commit6」で同じファイルの同じ箇所が修正されている場合、どちらの内容を優先して採用すればよいのか、「Git」は判断することができません。「Commit3」の「readme.txt」に以下のテキストが記述されているとします。
今日の天気は晴れです。
「Commit5」の「readme.txt」に以下のテキストが記述されているとします。
今日の天気は雨です。
「Commit6」の「readme.txt」に以下のテキストが記述されているとします。
今日の天気は雪です。
この時「Commit3」の「readme.txt」は、「Commit5」のテキストを採用すればいいのか、「Commit6」のテキストを採用すればいいのか、どちらがユーザーが期待するテキストなのか「Git」は判断できません。
マージの中止
マージする際、「マージコンフリクト」が発生すると、マージ処理を中止します。「Git」は「マージコンフリクト」が発生したファイルを把握しており、そのファイルには「コンフリクトマーカー(standard conflict-resolution marker)」が挿入されます。
ユーザーは、「マージコンフリクト」が発生したファイルを編集し、自分でコミットを行います。
これでマージが完了します。