226
228

More than 5 years have passed since last update.

Gitマージの基本 マージ・競合・競合解決・マージしなおし

Last updated at Posted at 2015-02-03

オライリー・ジャパン『実用Git』を最近読み終えたので、
マージについて、実用的な部分をつらつらとまとめておく。
時間があればマージだけでなく、コミットの変更方法についても後日書くかも。

マージ

マージ方法

マージするにはマージされる(他ブランチのコードを取り込む)側のブランチをチェックアウトして、
マージ対象(他のブランチにコードを取り込まれる)ブランチを指定して実行。
マージする時には余計なトラブルを避ける為に、未コミットのものがある状態でのマージは避ける。

git checkout ${マージされる側}
git merge ${マージ対象ブランチ}

マージ対象複のブランチは数指定可能。

# topic1,topic2のブランチがmasterにないコミットを含んでいる事を確認
$ git show-branch
* [master] a
 ! [topic1] b
  ! [topic2] c
---
  + [topic2] c
 +  [topic1] b
*++ [master] a
#マージ実行
$ git merge topic1 topic2
Fast-forwarding to: topic1
Trying simple merge with topic2
Merge made by the 'octopus' strategy.
 b | 1 +
 c | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 b
 create mode 100644 c
#マージ後にブランチの状態を確認すると、topic1,2の変更がマージされている事がわかる
$ git show-branch
* [master] Merge branches 'topic1' and 'topic2'
 ! [topic1] b
  ! [topic2] c
---
-   [master] Merge branches 'topic1' and 'topic2'
* + [topic2] c
*+  [topic1] b
*++ [topic2^] a

競合のあるマージ

例としてmasterにファイルhello.txtを用意し、topicブランチ作成後hello.txtに両ブランチで文言を追加し、masterにマージして競合を発生させる。


$ mkdir hello
$ cd hello/
$ git init
$ echo "hello from master" > hello.txt
$ git add hello.txt 
$ git commit -m "hello from master"
$ git checkout -b topic
#topicブランチで編集
$ echo "hello from topic" >> hello.txt 
$ git commit -am "hello from topic"
$ git checkout - 
#masterブランチでも編集
$ echo "hello from master" >> hello.txt  
$ git commit -am "hello from master again"
#同じファイルに対して、マージする、される側両方で編集していることを確認
$ git show-branch
* [master] hello from master again
 ! [topic] hello from topic
--
*  [master] hello from master again
 + [topic] hello from topic
*+ [master^] hello from master
$ git merge topic
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Automatic merge failed; fix conflicts and then commit the result.
#競合した
$ cat hello.txt 
hello from master
<<<<<<< HEAD
hello from master
=======
hello from topic
>>>>>>> topic
#diffをとってみる
$ git diff
diff --cc hello.txt
index 308c441,2f4e5d4..0000000
--- a/hello.txt
+++ b/hello.txt
@@@ -1,2 -1,2 +1,6 @@@
  hello from master
++<<<<<<< HEAD
 +hello from master
++=======
+ hello from topic
++>>>>>>> topic

競合後、git diffをとると++<<<<<<< HEADのように+が二重でかかっている。(場合によってはもっと増えることもある)左側がHEAD(マージされる側)に対するdiffで右側がtopic(取り込まれる側)に対してつけられたものなので注意。ちなみに、マージされる側の参照はMERGE_HEADで、マージ後に参照可能。
また、git diff HEADはgit diff --ours、MERGE_HEADは--theirsで、マージの起点からは--baseでdiffをとれる。

$ git diff HEAD
diff --git a/hello.txt b/hello.txt
index 308c441..9b4ba13 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +1,6 @@
 hello from master
+<<<<<<< HEAD
 hello from master
+=======
+hello from topic
+>>>>>>> topic

$ git diff MERGE_HEAD
diff --git a/hello.txt b/hello.txt
index 2f4e5d4..9b4ba13 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +1,6 @@
 hello from master
+<<<<<<< HEAD
+hello from master
+=======
 hello from topic
+>>>>>>> topic

$git diff --base
* Unmerged path hello.txt
diff --git a/hello.txt b/hello.txt
index 653f0d3..9b4ba13 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1,6 @@
 hello from master
+<<<<<<< HEAD
+hello from master
+=======
+hello from topic
+>>>>>>> topic

注意として、競合解決後オプション無しのgit diffでは、どちらかの候補を選ぶだけの解決をするとdiffがでなくなる仕様になっています。

$ git diff
diff --cc hello.txt
index 308c441,2f4e5d4..0000000
--- a/hello.txt
+++ b/hello.txt
@@@ -1,2 -1,2 +1,6 @@@
  hello from master
++<<<<<<< HEAD
 +hello from master
++=======
+ hello from topic
++>>>>>>> topic

#HEADを採用
$ vi hello.txt 
$ cat hello.txt 
hello from master
hello from master
#diffが出ないので注意
$ git diff
diff --cc hello.txt
index 308c441,2f4e5d4..0000000
--- a/hello.txt
+++ b/hello.txt

競合がどこにあるかをチェックする場合は、git statusかgit ls-files -uを行う。

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

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

    both modified:      hello.txt

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

$ git ls-files -u
100644 653f0d3a57048c82d25251ba8802d42bf2dcbd0e 1   hello.txt
100644 308c441c01f3e2a5d4fa5147ed5261d782af009f 2   hello.txt
100644 2f4e5d4f8d84256cecd64825ce364c2e3ca97da7 3   hello.txt

git ls-files -uをする事で競合のあったファイルのステージ番号がでる。123と番号があるのはそれぞれ以下の通り。

  1. マージの起点(base)
  2. マージ対象(ourバージョン)
  3. マージされる側(theirバージョン)

それぞれgit cat-file や git diff で参照できる。

$ git cat-file -p :1:hello.txt 
$ git cat-file -p :2:hello.txt 
$ git cat-file -p :3:hello.txt 
$ git diff :2:hello.txt :3:hello.txt 

競合解決

通常であれば、競合したファイルを編集後、ステージ(git add)してcommitすれば良い。(マージマーカなどが残った状態でもこの手順でgitには競合を解決したとして、commit可能になるので注意。

また、修正方法がourバージョンかtheirバージョンに統一するだけであれば

$ git checkout --ours hello.txt
$ git checkout --theirs hello.txt

としてcommitすることも可能。

マージ中断

マージ後(ただし、マージ完了をコミットしていない状態で)マージ前の状態に戻したい場合は

$ git reset --hard HEAD

マージ後(マージ完了をコミットした状態で)マージ前に戻したい時は以下のようにする

$ git reset --hard ORIG_HEAD

226
228
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
226
228