ブランチを使用する

Git merge

マージは、Git において分岐した履歴を戻して統合する手段です。git merge コマンドは、git branch を使用して作成された、独立した複数の開発ラインをひとつのブランチに統合するコマンドです。

ここで、次に説明するすべてのコマンドは現在のブランチへのマージを行うものであることに留意してください。現在のブランチはマージの結果更新されますが、対象ブランチはそのまま残ります。従って、git merge は通常、現在のブランチを選択する git checkout および不要になった対象ブランチを削除する git branch -d と併用されます。

仕組み

git merge は複数の連続するコミットを 1 つの履歴に統合します。git merge を使って 2 つのブランチを統合するのが最も一般的な使い方です。このドキュメントの以降の例では、ブランチのマージ方法を複数取り上げていきます。これらのシナリオでは、git merge は 2 つのコミット ポインター、(通常はブランチの先端) を取り、それらのポインター間に共通するベース コミットを見つけます。Git が共通するベース コミットを見つけると、新しい「マージ コミット」を作成し、キューに入っているそれぞれのマージ コミット シーケンスの変更を結合します。

main ブランチに基づく新しいブランチ フィーチャーがあるとします。これからこの feature ブランチを main にマージしていきます。

このコマンドを呼び出すと、指定したブランチ フィーチャーを現在のブランチ、つまり main にマージします。Git ではマージ アルゴリズムを自動で判定します (詳細は後述)。

新しいマージのコミット ノード

マージコミットには 2 つの親コミットがあるという他のコミットにはない特徴があります。マージコミットを作成すると、別々に分かれた履歴をまるで魔法のように自動的にマージします。両方の履歴で変更されたデータがあった場合、それらを自動的に結合することはできません。これはバージョン管理の競合を防ぐためであり、このような状況になった場合は続行する前にユーザーの確認が求められます。

マージの準備

マージを実行する前に、マージをスムーズに行うための準備作業がいくつかあります。

マージ先ブランチの確認

git status を実行して、HEAD が目当てのマージ先ブランチに向いていることを確認します。必要に応じて git checkout <マージ先> を実行して、マージ先ブランチに切り替えます。この例では git checkout main を実行します。

最新のリモートコミットのフェッチ

マージ先ブランチとマージ元ブランチに最新のリモート変更が反映されていることを確認します。git fetch を実行して最新のリモート コミットを取得します。フェッチが完了したら、git pull を実行して main ブランチに最新の更新が反映されていることを確認します。

マージ

前に説明した「マージの準備」手順が完了したら、git merge <ブランチ名> を実行してマージを開始します。<ブランチ名> はマージ先のブランチにマージするブランチの名前にします。

早送りマージ

早送りマージは、現在のブランチの先端から対象ブランチに向かって 1 本の直線的なパスのみが通っている場合に適用されます。この場合は「実際に」マージが行われるわけではなく、単に現在のブランチの先端を対象ブランチの先端に移動させることによって (即ち「早送り」によって) 統合する必要があります。これによって実質的に履歴が結合されて、対象ブランチからアクセス可能であったすべてのコミットが現在のブランチから利用できるようになります。たとえば、一部の機能を main に早送りマージすると、次のようになります。

main ノードの前にあるフィーチャー ノードは、早送り後はどちらも同じノード上になります

ところが、ブランチが分岐している場合は早送りマージを適用することはできません。対象ブランチに向かうパスが複数あるため、履歴を結合する手段は三方向マージ以外にはありません。三方向マージでは、2 つの履歴を結合するために専用のコミットが生成、実行されます。この名称は、3 つのコミット (2 つのブランチのそれぞれ先端のコミットとその共通の祖先であるコミット) を使用してマージコミットを生成することから来ています。


この 2 つのマージ戦略はどちらも使用可能ですが、多くの開発者は、小規模なフィーチャーやバグ修正には (リベースを使用した) 早送りマージを、長期にわたって行われたフィーチャーには三方向マージを使用する傾向があります。後者の場合、生成されたマージ コミットは 2 つのブランチのシンボリック結合として機能します。

最初に早送りマージの例を示します。次のコードでは、新規ブランチを作成し、それに 2 つのコミットを加え、最後に早送りマージによってそれを master ブランチに統合しています。

# Start a new feature
git checkout -b new-feature main
# 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 main
git merge new-feature
git branch -d new-feature

これは、(長期のフィーチャー開発を行うための大規模組織向けのブランチではない) 独立した小規模開発に向けた短期間のフィーチャーブランチを用いる場合の一般的なワークフローです。

また、new-feature ブランチは main ブランチからアクセス可能となるため、git branch -d コマンドを使用しても警告は表示されないはずです。

記録保持目的で早送りマージ中にマージのコミットが必要なケースでは、--no-ff オプションを付けて git merge を実行できます。

git merge --no-ff <branch>

このコマンドでは、指定したブランチを現在のブランチにマージしますが、その際に常に (たとえそれが「早送り」であっても) マージコミットを作成してマージします。このコマンドは、リポジトリにおいて発生したすべてのマージを記録する場合に有用です。

三方向マージ

次の例は非常によく似ていますが、フィーチャーの進行中に main が進行するので 3 方向マージが必要です。このシナリオは、大規模フィーチャーの場合やプロジェクトにおいて複数の開発者が同時に作業を行っている場合によく起こります。

Start a new feature
git checkout -b new-feature main
# 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 main branch
git checkout main
# Edit some files
git add <file>
git commit -m "Make some super-stable changes to main"
# Merge in the new-feature branch
git merge new-feature
git branch -d new-feature

この場合は後戻りをすることなく mainnew-feature に移動できないため、早送りマージの実行が不可能であることにご留意ください。

ほとんどのワークフローでは、new-feature は実際には開発に長期間を要する大規模フィーチャーであることが多く、その間に main にも新たなコミットが追加されることがよくあります。feature ブランチが実際にも上の例のように小規模である場合は、main にリベースして早送りマージする方を選択するようにします。この方法は不必要なマージ コミットを減らして、プロジェクト履歴の複雑化を防止します。

競合の解決

マージしようとしている 2 つのブランチにおいて同一ファイルの同一部分に変更が加えられている場合、どちらのバージョンを使用すべきかを Git 側で判断することはできません。そのような状況になった場合、Git はマージコミット生成の手前で停止するため、ユーザーは手作業で競合を解決できます。

Git のマージ プロセスが優れているのは、編集/ステージ/コミットという一般的なワークフローを使用してマージの際の競合の解決できる点です。マージの際に競合が発生した場合は、git status コマンドを実行すると競合を解決すべきファイルが表示されます。たとえば、両方のブランチで hello.py の同一の部分が変更されていた場合、次のような情報が表示されます。

On branch main
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
both modified: hello.py

競合の表示方法

マージ中に Git で競合が発生すると、影響が出ているファイルの内容が編集され、両方のファイルで競合が発生している箇所に印が付きます。こうした印として「<<<<<<<」、「=======」、「>>>>>>>」が使用されます。マージ中に競合の解決が必要な場合はプロジェクト内でこうした印を検索すると便利です。

here is some content not affected by the conflict
<<<<<<< main
this is conflicted text from main
=======
this is conflicted text from feature branch
>>>>>>> feature branch;

通常、======= の前のコンテンツはマージ先のブランチで、その後がマージ元のブランチです。

競合するセクションを特定したら、マージを自分の好みに合わせて修正できます。マージを完了する準備ができたら、競合したファイルに対して git add を実行して、競合が解決したことを Git に通知します。続いて通常の git commit を実行してマージ コミットを生成します。このプロセスは通常のスナップショットのコミットとまったく同一であるため、一般の開発者にとってマージ作業は分かりやすいものとなっています。

なお、マージの際の競合が起こるのは三方向マージの場合のみであることに留意してください。早送りマージでは変更が競合を起こすことはありません。

概要

このドキュメントでは、git merge コマンドの概要を取り上げました。マージは Git に欠かせないプロセスです。マージの仕組み、および早送りマージと三方向マージ、通常のマージの違いについて説明しました。以下が主なポイントです。

  1. git merge は連続するコミットを 1 つの履歴に統合します。
  2. git がマージを実行する主な方法は、早送りマージと三方向マージの 2 つです。
  3. git は、両方のコミット シーケンスに競合する変更がない限り、コミットを自動でマージできます。

このドキュメントは、git branchgit pullgit fetch などのドキュメントの内容を統合、参照して作成しています。詳細については各コマンドのページを参照してください。

ブランチを試す準備が整いましたか?

この対話式チュートリアルを利用しましょう。

今すぐ始める