マージ競合とは
ここでは、開発者が 2 つの重複するソースから最適な結果を生み出すのにマージの競合の解決がどのように役立つかを説明します。
GitHub フロー
GitHub では、ソフトウェアの共同開発用のプラットフォームを提供するだけでなく、該当する各種機能の使用を最適化するように設計された規定のワークフローも用意されています。 このユニットでは、特にマージ競合について説明しますが、まずは「GitHub フローについて」を確認することをお勧めします。
ブランチのマージ
開発者が main
に基づいて feature-branch
という名前のブランチを作成し、2 つのコミットを作成するシナリオについて考えてみます。 この作業が行われているときに、他の誰かが関連のないプル要求を main
にマージします。 開発者が feature-branch
を再び main
にマージしようとするとどうなりますか。
答え: "場合によります"。
feature-branch
は main
から作成されましたが、そのブランチ自体をベースにはしていません。 むしろ、その時点で main
の "HEAD コミット" に基づいていました。 これは、それ以降に main
に適用されたコミットをどれも認識していません。 これが現在追跡しているコミットは、そのブランチの現在の状態に、最近の変更を上書きすることなしに組み込まれるとは限りません。
feature-branch
のコミットが、ブランチの作成以降、main
に対して行われた並列コミットと重複しないことが判明した場合、問題はありません。 新しいファイルを追加できます。 手付かずのファイルは削除できます。 main
内で変更されたコード行は、feature-branch
が作成されてから並列処理によって変更されていない限り、feature-branch
内で変更することができます。
しかし、両方のコミット セットに同じコード行への変更が含まれている場合はどうでしょうか。 このマージの試みは、マージ競合が原因で失敗します。
マージ競合とは
マージ競合は、開発者が変更のマージを試みたとき、それによって並列変更が誤って上書きされる場合に発生します。 それらの他の変更がどのようにベース ブランチにマージされたかは問題ではありません。 Git が、他方を優先して一方の変更セットを自動的に上書きすることはありません。 代わりに、マージを試みている人物に対してそれらを指摘し、その人物が比較ブランチでそれらを解決してからマージを再度試みることができるようにします。
マージ競合の解決
マージの競合の解決を支援するために、GitHub は、各ブランチとの差分を含む一時的なハイブリッド ファイルを生成します。 規則として、比較ブランチからのテキストは、ベース ブランチの上に表示され、等号 (=======
) の行で区切られます。
変更がわずかな場合は、このビューを使用してファイルを直接編集できます。 それを保持することに決めた場合、最終的な結果が比較ブランチにコミットされます。 あるいは、マージがより複雑な場合、あなたは他の開発ツールを使用してその作業を行いたいと考えるかもしれません。 どちらの方法でも、コミットする前に、コードからブランチ マーカーを削除することを忘れないでください。 競合の解決をコミットするときにこれらのマーカーを削除するのを忘れた場合、それらはファイル内に残り、コメントアウトされません。
Note
このユニットでは、ブラウザーのコンテキスト内でのマージ競合の解決について説明します。 また、統合されたマージ競合解決エクスペリエンスを備えた開発プラットフォームも多数あります (Visual Studio など)。
ブランチ上でマージの競合がすべて解決されたら、マージを再試行できます。
マージ競合の回避
特定のマージ競合は避けられません。 マージを行うと、承認されるのを待機しているその他のプル要求に対してマージ競合が発生する可能性があります。 ただし、マージ競合の複雑さを軽減する効果的な方法の 1 つは、ご利用のブランチを頻繁にプルすることです。
早期および頻繁なプル
git pull
コマンドは、現在のブランチにまだ適用されていないベース ブランチ コミットがあれば、それをプルしてきます。 概念的には、多くのバージョン コントロール システムで使用される Get Latest コマンドと似ています。これを使用すれば、ご利用のローカル コードを最新バージョンに更新できるようになります。 ブランチに対して更新をプルすると、そのブランチが作成された後に (または最後にプルが実行された後に) 発生したすべての変更をマージすることになります。
ブランチに更新をプルすると、マージの競合が発生する可能性がありますが、それは問題ありません。 今せずとも後でそれらを取得することになるでしょう。それらを早く取得することで、多くの場合、対処が容易になります。
マージ競合の影響を軽減することに加えて、更新プログラムをプルすることでも、作業しながら、コミットされた変更をご利用のブランチに統合することができます。 そうすることで、潜在的な問題を早期に回避できます。 たとえば、他のファイル内にクラス定義の変更があり、それが原因でコードがコンパイルできなくなる場合があります。 この変更は、後でマージを行ったとしてもマージの競合を引き起こしませんが、最初にそれをテストしないと、ビルドを破壊することになります。 ベスト プラクティスとして、更新プログラムを頻繁にプルして、ご利用のブランチを可能な限りそのベースに近づけることをお勧めします。
Git リベースで履歴を整理する
git rebase
(または git pull --rebase
) コマンドでは、ご利用のブランチ履歴を書き換えて、そのベースとしてベース ブランチの現在の HEAD コミットを使用できるようにします。 つまり、ブランチは、ベース ブランチの現在の状態からのみ分岐しているものとして動作するように更新されます。 このリベースは、すべての変更が、最初に分岐した元のコミットではなく、ベース ブランチの最新の状態と比較されることを意味します。 リベースでは、あなたのコミットが過去の並列コミットの後に直線的な形で続くようになるため、最終的なマージ後に履歴が追跡しやすくなります。 上流をマージする直前にご利用のブランチをリベースすることをお勧めします。
詳細については、「Git リベースについて」と「Git リベース後のマージ競合の解決」を参照してください。