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 の公式ドキュメントです。