Git コンフリクトの解消

Gitクラウド

前回までで Git 環境の構築および実際のワークフローの運用手順までを説明しました。
今回はマージの際にコンフリクトが発生した場合の対処方法について説明いたします。

参考: Git を利用した開発環境・テスト環境・本番環境の構成
参考: Git ワークフロー(ブランチモデル)とその手順


追跡ブランチを最新の状態にするため pull したときにコンフリクトが発生するパターン
基本的にコンフリクトはブランチのマージで発生します。
git pullgit fetch + git mergeですから、当然git pullでもコンフリクトは発生します。

例えばリモートのorigin/developブランチを自身のローカル環境にgit pullしようとしており、origin/developにはすでに2つの新たなコミットが存在していた場合を想定します。

参考: ③ ローカルの develop ブランチの内容を最新にする

>git checkout develop
>git status
On branch develop
Your branch and 'origin/develop' have diverged,
and have 3 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

コミットの完了していないファイルがコンフリクト(の前段階的なエラー)を起こすパターン
ローカルにコミットの完了していない作業ブランチがあります。

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test.php

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

このとき、これからgit pullしようとしているorigin/develpの中にコンフリクトを起こす可能性のあるファイルが含まれていた場合(同じファイルを編集していた場合)、以下のようなエラーメッセージが出力されます。

>git pull origin develop
From xx.xx.xx.xx:/path/to/remote
 * branch            develop    -> FETCH_HEAD
Updating 6432796..fc320d3
error: Your local changes to the following files would be overwritten by merge:
        test.php
Please commit your changes or stash them before you merge.
Aborting

メッセージ中に「CONFLICT」の文字が出てこないので何が起こったのかわかりにくいですが、このタイミングでこのメッセージが出力されたときは、コンフリクトが発生する前段階だと思って良いかと思います。
この場合、以下のような対処方法が挙げられます。

・編集中のファイルを一旦コミットする(修正を完了させてからコミットする)
・ローカル環境の変更されたファイルを元に戻す

これで一旦はorigin/developgit pullは成功するようになりますが、問題を先送りしているだけなので、次のステップ、すなわち作業ブランチをdevelopブランチへマージする際に実際にコンフリクトが発生します。
実際にコンフリクトが発生した場合の対処方法については下の「作業ブランチを追跡ブランチへマージする際にコンフリクトが発生するパターン」で説明いたします。

以下はローカル環境の編集内容を元に戻すときのコマンド例です。

# ローカル環境の編集内容を元に戻す
>git checkout test.php # git checkout [対象ファイル名]

なお、git stashコマンドによるブランチの退避や、git resetによる強制上書きなどの方法もありますが、あまりお勧めではありませんのでここでは説明を割愛いたします。

追跡ブランチにいくつかマージがあり、リモートのブランチとコンフリクトが発生するパターン
ローカルのdevelopブランチにいくつかの作業ブランチをすでにマージ済みで、これからgit pullしようとしているorigin/developの中にコンフリクトを起こす可能性のあるファイルが含まれていた場合(同じファイルを編集していた場合)、以下のようなエラーメッセージ出力されます。

>git checkout develop
>git pull origin develop
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (4/4), 366 bytes | 366.00 KiB/s, done.
From xx.xx.xx.xx:/path/to/remote
 * branch            develop    -> FETCH_HEAD
   fc320d3..10199df  develop    -> origin/develop
Auto-merging test.php
CONFLICT (content): Merge conflict in test.php
Automatic merge failed; fix conflicts and then commit the result.

明確にコンフリクトが起きたというメッセージが出力されました。
実際にコンフリクトが発生した場合の対処方法についてはすぐ下の「作業ブランチを追跡ブランチへマージする際にコンフリクトが発生するパターン」で説明いたします。


作業ブランチを追跡ブランチへマージする際にコンフリクトが発生するパターン
例えば、作業ブランチの修正が完了し当該ブランチをローカルのdevelopブランチにマージしようとします。

参考: ④ 作業ブランチの内容をローカルの develop ブランチにマージする

>git checkout develop
>git merge --no-ff feature/iss04
Auto-merging test.php
CONFLICT (content): Merge conflict in test.php
Automatic merge failed; fix conflicts and then commit the result.

test.phpでコンフリクトが発生した旨のメッセージが出力されました。
状態を確認してみましょう。

>git status
On branch develop
Your branch is up to date with 'origin/develop'.

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:   test.php

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

コンフリクトが発生したファイルの中身を確認すると、Git が修正前と修正後の違いにマークをつけてくれていることがわかります。

<?php

<<<<<<< HEAD

[マージ先のコード]

=======

[マージ元のコード]

>>>>>>> feature/iss04

?>

<<<<<<< HEADが違いの部分の開始行で、>>>>>>> [ブランチ名]の部分が終了行です。
=======の上がマージ先(修正前)のコード、下がマージ元(修正後)のコードになります。
この結果を踏まえて、違いを確認しながらコードを編集していきます。
この辺りは、Git クライアントツールなどを利用するととても編集しやすいかと思います。
ちなみにこの状態になると、コンフリクトを解消してマージを完了させないと他のブランチに移動することもできません

>git checkout feature/iss04
test.php: needs merge
error: you need to resolve your current index first

編集が完了したら、git commitして完了です。

>vim test.php
>git add -A
>git status
On branch develop
Your branch is up to date with 'origin/develop'.

All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   test.php

>git commit -m "Conflict"
[develop dc8e9b3] Conflict

この状態で先ほどのマージコマンドも完了しています。

>git status
On branch develop
Your branch is ahead of 'origin/develop' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean


一旦元の状態に戻したい場合
コンフリクトが発生したので一旦コンフリクト前の状態に戻したいという場合は以下のようにします。

>git merge --abort # コンフリクトが起きた後、まだ何もしていない場合
>git reset --hard HEAD # コンフリクト解消のためのファイル編集やaddまで行った場合(編集内容も消える)


参考: Git を利用した開発環境・テスト環境・本番環境の構成
参考: Git ワークフロー(ブランチモデル)とその手順
参考: Git のフックを利用したデプロイの方法
参考: git push を Slack へ通知する方法

コメント