Git において、ブランチを使った開発では避けても通れないブランチのマージ。今回、Git でマージを行うコマンド「git merge」の仕様と基本的な使い方をまとめました。
目次
git merge の基本仕様
「git merge」は、現在のチェックアウトしているブランチに対して、別のコミットが持っている変更内容をマージ(統合)するためのコマンドです。
ここでの「コミットが持つ変更」とは、正確には「現在のブランチの履歴と、指定コミットの履歴とが分岐した地点から指定コミットまでに及ぼされた変更内容」という意味です。
下記の図では黄色いコミットで発生した変更のことですね。
git merge の使い方
git merge の基本的な仕様例を紹介します。どれもGit の運用においてはよく発生するケースではないでしょうか。
異なるブランチをマージする
git merge の最も基本的な使い方です。異なるブランチをマージします。
// ブランチの状態、現在masterブランチをチェックアウトしている。
o---o :topic
/
-o----o :master <- HEAD
マージを実行することで、新しいコミットが作られます。
// マージの実行
$ git merge topic
// 結果
o---o :topic
/ \
-o----o----o :master <- HEAD
Fast-forward(早送り)マージについて
fast-forward(早送り)マージとは?
もし、マージしたいブランチ(topic)が現在のブランチ(master)の直接的に先にす進んでいる場合、マージにより新しくマージコミットを生成する必要がありません。(そのコミットは、マージブランチ(topic)から見て、変更を含まない空のコミットとなるからです。
// ブランチの状態、現在masterブランチをチェックアウトしている。
o---o :topic
/
-o :master <- HEAD
--no-ff オプションによるfast-forward(早送り)マージを禁止
このような場合、Git は、新しいコミットを作らずに、参照のみを移動してマージを完了します。これを「Fast-forward(早送り)マージ」と呼びます。
// マージの実行
$ git merge topic
// masterの参照のみが移動する(Fast-forwardマージ)
o---o :topic, master <- HEAD
/
-o
このふるまいを避ける場合「--no-ff」オプションを加えることで、強制的に新しいコミットを生成することも可能です。
// --no-ff オプションで、マージの実行
$ git merge --no-ff topic
// 新しいコミットを必ず生成
o---o :topic,
/ \
-o --------o :master <- HEAD
マージコミットの取り消し方法
マージコミットを取り消します。もっとも簡単なものは、「git reflog」と「git reset」を利用し、強制的にHEAD参照を、過去のコミットに移動してしまう方法です。
// HEAD参照の移動を一覧
$ git reflog
// 出力: すべてのHEADの移動履歴が表示される。
18828ad HEAD@{0}: merge topic: Merge made by the 'recursive' strategy.
1c17f4f HEAD@{1}: checkout: moving from topic to master
4f07825 HEAD@{2}: commit: Add header.
1c17f4f HEAD@{3}: checkout: moving from master to topic
1c17f4f HEAD@{4}: commit: New comit .
3b1e279 HEAD@{5}: commit (initial): Inital
例えば、上記の例では、HEAD@{1}が、マージの直前の状態を表していますので、git reset を使って、強制的に参照、インデックスと作業ツリーのすべての状態を戻します。
// マージを取り消す。
$ git reset --hard HEAD@{1}
これにより、ブランチ履歴を過去の状態に戻すことができました。
git merge の取り消しに関しては、こちらの記事でも詳しく解説しています。
コンフリクト(競合)の発生と解決
マージに指定したブランチが、現在のブランチとの間に、競合する変更内容を持っていれば「コンフリクト」として、マージ処理がストップします。
// マージの実行
$ git merge topic
// コンフリクトによる中断
Auto-merging index.php
CONFLICT (content): Merge conflict in index.php
Automatic merge failed; fix conflicts and then commit the result.
Git は、自分でコンフリクトを解決しようとはせず、基本的には、開発者に解決を委ねます。
git merge の動作仕様
Git merge を実行すると、下記の仕様で、マージ処理が進みます。
- MERGE_HEAD 参照が、指定したブランチの先頭を指すように設定される。
- HEADが参照はそのまま。
- マージ処理を開始し、マージが問題なく成功したファイルの状態が、インデックスと作業ツリーにそれぞれ反映される。
- 競合が発生したファイルは、インデックスに3つのバージョンとして保存される。(履歴が分岐する前の共通の状態、HEADの状態、MERGE_HEADの状態)作業ツリーは、「<<<」「====」「>>>」の記号を使って、開発者に競合箇所を明示するように更新される。
- マージに関係ないファイルが変更されることはない。
1箇所以上のコンフリクトが発生すれば、処理が中断し、コンフリクトの解決が、開発者に委ねられます。
その他の git merge の主要なオプション
マージを中断する「--abort」
コンフリクトが生じ、コミットが行われていない状態で、マージを中断して git merge 実行前の状態に戻します。
マージを再開する「--continue」
こちらもコンフリクト発生時に、コードを編集して競合を解決した後に、このコマンドで、マージを完了するように支持します。
自動マージ方針を指定「-ours」「-theirs」
Git に自動マージを行ってほしいとき、競合を解決するヒントを与えることができます。「-ours」では、強制的に現在チェックアウトしているブランチの変更を優先し、逆に「-theirs」では、マージするように指定したブランチの変更を優先します。
参考情報
Git の公式ドキュメントです。