プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

Git MergeでConflictが発生したときの対処

異なるブランチで同じファイルの同じ場所を同時に編集するとConflictが発生する。

f:id:hogehoge666:20200828151728p:plain

Conflictが発生した状態でMergeしようとすると失敗する。

$ git merge feature -m "merge feature branch into master"
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.

Mergeに失敗したファイルにはConflictの内容がMarkerと一緒に挿入される。

$ cat hello.py 
def main():
<<<<<<< HEAD
    print("Hello World from master branch!")

def master_branch():
=======
    print("Hello World from feature branch!")

def feature_branch():
>>>>>>> feature
    pass
 
if __name__ == '__main__':
    main()
$

Mergeをやめる場合はgit merge --abortする。

それでもMergeしたい場合はConflictを解消後、add、commitする。

Conflict解消作業中にコードが壊れる可能性があるので解消後にはテストを再実施する。

以下にConflict解消作業の詳細を示す。


状況確認

コミットログを確認。
Mergeは完了していない。

$ git log --oneline
1b6bd11 (HEAD -> master) Modify hello.py on master branch
e93be91 (origin/master) Create hello.py
71bf522 first commit

Working DirectoryとStaging Areaの状態を確認。
hello.pyがmergeできていない。

$ git status
On branch master
<略>
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:   hello.py

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

Conflictの内容確認

git diffでどのファイルにどんなMarkerが挿入されたか雰囲気を掴む。
手軽だがworking directoryの状況とずれているので直感的に全体像を把握しにくい。

$ git diff
diff --cc hello.py
index 7a983db,b2bef2f..0000000
--- a/hello.py
+++ b/hello.py
@@@ -1,7 -1,7 +1,13 @@@
  def main():
++<<<<<<< HEAD
 +    print("Hello World from master branch!")
 +
 +def master_branch():
++=======
+     print("Hello World from feature branch!")
+ 
+ def feature_branch():
++>>>>>>> feature
      pass
   
  if __name__ == '__main__':
$ 

Makerが挿入されたファイルの中身を確認。

$ cat hello.py 
def main():
<<<<<<< HEAD
    print("Hello World from master branch!")

def master_branch():
=======
    print("Hello World from feature branch!")

def feature_branch():
>>>>>>> feature
    pass
 
if __name__ == '__main__':
    main()
$

原因になったCommitを確認

一覧表示。

$ git log --merge
commit fd201265f633ba617ef50a6f9bd19401ceae32d8 (feature)
Author: xxxxx <xxxxx@gmail.com>
Date:   Fri Aug 28 12:11:50 2020 +0900

    Modify hello.py on feature branch

commit 1b6bd11bae35867e566a85ec3bde3a59143ded08 (HEAD -> master)
Author: xxxxx <xxxxx@gmail.com>
Date:   Fri Aug 28 12:09:38 2020 +0900

    Modify hello.py on master branch
$

詳細表示。

$ git show 1b6bd11bae35867e566a85ec3bde3a59143ded08
commit 1b6bd11bae35867e566a85ec3bde3a59143ded08 (HEAD -> master)
Author: xxxxx <xxxxx@gmail.com>
Date:   Fri Aug 28 12:09:38 2020 +0900

    Modify hello.py on master branch

diff --git a/hello.py b/hello.py
index 2e45b94..7a983db 100644
--- a/hello.py
+++ b/hello.py
@@ -1,5 +1,8 @@
 def main():
-    print("Hello World!")
+    print("Hello World from master branch!")
+
+def master_branch():
+    pass
  
 if __name__ == '__main__':
     main()
$

Mergeをやめる

Markerなどが消えてGit Merge前の状態に戻る。
履歴も残らない。

git merge --abort

Conflictを解消する

masterブランチのhello.pyを採用。

git checkout --ours hello.py

featureブランチのhello.pyを採用。

git checkout --theirs hello.py

手動で編集。

$ vi hello.py 
def main():
    print("Hello World from master branch!")

def feature_branch():
    pass
 
if __name__ == '__main__':
    main()

コミット

変更をコミット。

git add hello.py
git commit -m "resolve conflict b/w master and feature branch"

確認。

$ git log --oneline
031d9a3 (HEAD -> master, origin/master) resolve conflict b/w master and feature branch
fd20126 (feature) Modify hello.py on feature branch
1b6bd11 Modify hello.py on master branch
e93be91 Create hello.py
71bf522 first commit

Pull RequestでConflictが発生した場合

Conflict発生

Pull RequestでConflictが発生すると以下のように表示される。
Conflictを解消するまでMergeボタンが非活性化されている。
WebUI上で解消する方法とLocal Repositoryで解消してPushする2つの方法がある

f:id:hogehoge666:20200828151744p:plain

1. WebUI上でConflictを解消

WebEditorで解消してMark as resolvedボタンを押すとMergeボタンが活性化する。

f:id:hogehoge666:20200828151759p:plain

2. Local RepositoryでConflict 解消
  1. featureブランチを最新化
  2. featureブランチにmasterをMerge
  3. Conflictを解消してコミット
  4. masterにfeatureブランチをMerge --no-ff
  5. Push

git pullでConflictが発生した場合

Conflict発生

既にGitにRepositoryが作成されている状況で、ローカルにもレポジトリをgit initで作成して作業したとする。 作業後、git pushする前にgit pullを実行すると以下のようにmergeでエラーが出る。

$ git pull origin main
From https://github.com/<username>/<repository>
 * branch            main       -> FETCH_HEAD
fatal: refusing to merge unrelated histories
$

ファイルにConflictのMarkerすら挿入されないのでこのままではなにもできない。

1. 強制的にmerge + Conflict発生

こういう時に強制的にmergeをさせるのが「allow-unrelated-histories」だ。

まずはfetchする。

$ git fetch
$

次に「allow-unrelated-histories」付きでmergeする。

$ git merge --allow-unrelated-histories origin/main
Auto-merging hello.py
CONFLICT (add/add): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.
$ 

mergeが実行されConflictが発生した。

Conflict 解消

後は今まで述べた手順でConflictを解決してcommit、pushすれば解決だ。

例えばローカルで編集した内容を優先するなら下記のようにする。

git checkout --ours hello.py
git add .
git commit -m "resolved conflict"
git push