git merge でのコンフリクト(競合)の解決方法まとめ

最終更新:2018-06-22 by Joe

git merge のコンフリクトの解決方法についてです。gitでブランチを利用し、git mergeを使うようになったら、競合(コンフリクト)とその解決の作業を避けて通れません。

今回、コンフリクトの解決方法についてまとめました。

git merge について

git merge は、現在のブランチに対して、特定のコミットまでの歴史を統合します。実際の使用では、しばしばブランチ名を指定します。ブランチ名は、そのブランチの先頭コミットへの「参照」ですので、実際に指定されるのはコミットです。

// ブランチ topic を現在のブランチにマージする
$ git merge topic

具体的には、例えば、下記のマスターブランチ「G」に対して、topicブランチをマージすると・・・

	  A---B---C topic
	 /         
    D---E---F---G master

// masterに、topicブランチをマージする
$ git merge topic

このように、新しいコミットを「H」が結果として生成されます。A、B、Cにおける変更が、masterブランチに取り込まれます。

// 結果

	  A---B---C topic
	 /         \
    D---E---F---G---H master

git merge コマンドの基本仕様に関しては、こちらの記事もご確認下さい。

git merge におけるコンフリクト(競合)

変更内容をマージにより統合する際に、もし同じファイルの重複する部分が変更されていると、変更同士が衝突し「コンフリクト」と呼ばれる状態を引き起こします。

// マージの実行
$ git merge branch-B

// 出力:コンフリクトによる中断を伝える
Auto-merging index.php
CONFLICT (content): Merge conflict in index.php
Automatic merge failed; fix conflicts and then commit the result.

2つの履歴のうち、どっちらのコード変更を採用すればいいの?という問題を、Git自身が決めることはできないので、開発者が指定したり、次コードを調整したりする必要があるのです。

git mergeの実行時に、コンフリクトが起きると、git mergeの処理は一時停止され、開発者にその後の処理を委ねます。これはGit において特別な状態で「merge の中断状態」となります。この時、開発者はいずれかを選択することになります。

  • コード上のコンフリクトを解決して、git commit の実行により、マージコミットをコミットする
  • いったんマージを中止して元に戻す

上記のいずれかの手段を行う以外は、他の Git 操作ができません。

Git でのコンフリクトの解消方法

それでは、解決を試みます。

1コンフリクト箇所の確認する

git merge でコンフリクトが発生した直後に、「git status」を行えば、コンフリクトの箇所が分かります。

// ステータスを確認
$ git status

// 出力
On branch branch-A
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

	both modified:   index.php

no changes added to commit (use "git add" and/or "git commit -a")

Unmerged path に、コンフリクトが発生したファイルが列挙されています。

2必要箇所を修正する

コンフリクトしているファイルが分かったら、直接ファイルを開いて修正していきます。

git merge の実行時に、コンフリクト箇所は、作業ツリーのファイル内で、「<<<< HEAD」「====」「 >>>> topic」によってハイライトされていますので、ファイル内で「>>>>」などの連続を検索すると、問題の箇所がすぐに見つかるでしょう。

<div class="content-area front">

<<<<<<< HEAD
  <h1>This is added from branch-A</h1>
=======
  <h1>This is added from branch-B</h1>
>>>>>>> branch-B

慣れていないとちょっとぎょっとしますが、恐れる事はありません。HEADが、現在のブランチです。その下が「git merge branch-B」 と対象に指定したブランチです。

作業ツリー内のファイルに Git 自身が書き込みを行ったものです。これをエディタで、不要な部分を消して綺麗に整えます。

3修正をインデックスにステージし、コミットしなおす

インデックスにはまだ、マージ前のファイル状態が記録された状態です。競合を修正し終わったファイルを、git add します。

$ git add index.php

もう一度、git status を確認します。

$ git status

// 出力
On branch branch-B
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

	modified:   index.php

Gitが「All conflicts fixed but you are still merging.」と言っています。

あなたがコンフリクトを解消して、git add を実行して、修正をインデックスにステージした事に気づいています。あとは、git commit を実行すれば、競合を解決したマージコミットの完成です。

$ git commit

// エディタでメッセージを編集・・
Merge branch 'branch-B' into branch-A

# Conflicts:
#       index.php
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#       .git/MERGE_HEAD
# and try again.

無事、マージコミットが作成されました。

*   2787f68 (HEAD -> refs/heads/branch-A) Merge branch 'branch-B' into branch-A
|\  
| * 9e2f7d0 (refs/heads/branch-B) fix something again.
* | 733fc68 fix something.
|/  
* 1034887 (refs/heads/master) Update something again. 
* 9031694 Update something.

最後の git commit の成功をもって、git merge の処理が完了します。

上記では、git add git commit を手動で実行しましたが、これの代わりに「git merge --continue」を実行すれば、git add と git commit を自動的に実行してくれます。どちらでも結果は一緒です。

git merge の取り消し

コンフリクトの修正が難しいなどの理由で、一度実行したgit merge も、途中で取り消して中止することができます。

$ git merge --abort

以上、git merge のコンフリクトの解決についてでした。

参考リンク

Git merge の仕様については、こちらの記事もご覧ください。

Git の公式ドキュメントです。