ブランチを使用する

Git merge

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

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

仕組み

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

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

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

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

マージの準備

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

マージ先ブランチの確認

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

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

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

マージ

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

早送りマージ

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

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


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

最初に早送りマージの例を示します。次のコードでは、新規ブランチを作成し、それに 2 つのコミットを加え、最後に早送りマージによってそれを 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 コマンドを使用しても警告は表示されません。

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

git merge --no-ff <ブランチ>

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

三方向マージ

次の例は前の例とよく似ていますが、フィーチャーの進行中に 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"
# 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

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

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

競合の解決

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

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

On branch master
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
<<<<<<< master
this is conflicted text from master
=======
this is conflicted text from feature branch
>>>>>>> feature branch;

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

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

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

概要

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

  1. Git のマージでは連続したコミットを 1 つのコミット履歴に統合する。
  2. Git には主に、早送りと三方向の 2 つのマージ方法がある。
  3. Git では、コミット元とコミット先の両方に競合する変更がない限り、自動的にコミットをマージする。

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

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

この対話式のチュートリアルをお試しください。

今すぐ始める