Version Control with Git ch9 Merges (1/3)

Git勉強メモ

出典: 


Merges

  • git merge

    • 単一リポジトリ内の2つ以上のブランチを統合する
    • 多くの場合2つ
  • コンフリクト

    • 同一ファイルの同一行の変更が競合
    • 発生したら、“unmerged”としてインデックスにマークされる
    • 解消は開発者に委ねられる

Merge Examples

git checkout branch # マージ先をチェックアウト
git merge other_branch # other_branchをbranchにマージ

Preparing for a Merge

  • cleanな状態で始めたほうがいいよ

    • clean working directory
    • clean index
  • 【補】v2.17.1で、cleanじゃないとそもそもmergeできない感じだった

Merging Two Branches

  • こんな状態
git log --branches --graph --oneline
* 3b21657 (alternate) Add alternate's line 4
| * 1cc6004 (HEAD -> master) Another file
|/  
* a1225b9 Initial 3 line file
  • 2つの変更は衝突していない
git diff master alternate
diff --git a/file b/file
index fe999ef..a3c4be1 100644
--- a/file
+++ b/file
@@ -1,3 +1,4 @@
 Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
+Line 4 alternate stuff
diff --git a/other_file b/other_file
deleted file mode 100644
index eaeeeba..0000000
--- a/other_file
+++ /dev/null
@@ -1 +0,0 @@
-Here is stuff on another file!
  • マージはスムーズに済む
git merge alternate
Merge made by the 'recursive' strategy.
 file | 1 +
 1 file changed, 1 insertion(+)
  • コミットグラフはこうなる
git log --graph --pretty=oneline --abbrev-commit
*   7a79684 (HEAD -> master) Merge branch 'alternate'
|\  
| * 3b21657 (alternate) Add alternate's line 4
* | 1cc6004 Another file
|/  
* a1225b9 Initial 3 line file
  • git merge other_branchは、「今いるブランチにother_branchをマージする」の意

    • 今いるブランチしか影響を受けない
    • この操作ではalternateブランチは影響を受けていない

A Merge with a Conflict

git show-branch
* [alternate] Add alternate line 5 and 6
 ! [master] Add line 5 and 6
--
*  [alternate] Add alternate line 5 and 6
 + [master] Add line 5 and 6
*+ [alternate^] Add alternate's line 4
  • 5,6行目がコンフリクト

    • master: line5,6追加
    • alternate: alternate line5,6追加
git diff master alternate
diff --git a/file b/file
index c063061..1a7b51b 100644
--- a/file
+++ b/file
@@ -2,5 +2,5 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
-Line 5 stuff
-Line 6 stuff
+Line 5 alternate stuff
+Line 6 alternate stuff
diff --git a/other_file b/other_file
deleted file mode 100644
index eaeeeba..0000000
--- a/other_file
+++ /dev/null
@@ -1 +0,0 @@
-Here is stuff on another file!
  • merge中断される
git checkout master
git merge alternate
Auto-merging file
CONFLICT (content): Merge conflict in file
Automatic merge failed; fix conflicts and then commit the result.
  • コンフリクト
git diff
diff --cc file
index c063061,1a7b51b..0000000
--- a/file
+++ b/file
@@@ -2,5 -2,5 +2,10 @@@ Line 1 Stuf
  Line 2 Stuff
  Line 3 Stuff
  Line 4 alternate stuff
++<<<<<<< HEAD
 +Line 5 stuff
 +Line 6 stuff
++=======
+ Line 5 alternate stuff
+ Line 6 alternate stuff
++>>>>>>> alternate
  • コンフリクトをどう解消するかは開発者次第
  • 今回はこうした
(編集)
git add file
git diff --cached
diff --git a/file b/file
index c063061..89e2b8f 100644
--- a/file
+++ b/file
@@ -3,4 +3,4 @@ Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
 Line 5 stuff
-Line 6 stuff
+Line 6 alternate stuff
  • git commitするとマージコミットのテンプレートメッセージがEDITORで開く
Merge branch 'alternate'

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


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#	modified:   file
#
  • コミットグラフはこうなる
*   ffb7c83 (HEAD -> master) Merge branch 'alternate'
|\  
| * 152d410 (alternate) Add alternate line 5 and 6
* | bf387d7 Add line 5 and 6
* |   7a79684 Merge branch 'alternate'
|\ \  
| |/  
| * 3b21657 Add alternate's line 4
* | 1cc6004 Another file
|/  
* a1225b9 Initial 3 line file

Working with Merge Conflicts

Locating Conflicted Files

  • git statusでコンフリクトが発生しているファイルを特定できる
git status
On branch tmp
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:   file

no changes added to commit (use "git add" and/or "git commit -a")
  • git ls-files -u でblobを特定できる
git ls-files -u
100644 a3c4be15bb92e24db3cc4a2db0aa889148a93afe 1	file
100644 c06306161c8c2bd4a16da3f6a3e7e130b9fcecca 2	file
100644 1a7b51bee6459775eb9f30df455ea327fe0d99fe 3	file
git cat-file -p a3c4be15
git cat-file -p c0630616
git cat-file -p 1a7b51be
Line 1 Stuff
Line 2 Stuff
Line 3 Stuff
Line 4 alternate stuff
Line 1 Stuff
Line 2 Stuff
Line 3 Stuff
Line 4 alternate stuff
Line 5 stuff
Line 6 stuff
Line 1 Stuff
Line 2 Stuff
Line 3 Stuff
Line 4 alternate stuff
Line 5 alternate stuff
Line 6 alternate stuff
  • git diffでむちゃくちゃ詳細にわかる
git diff
diff --cc file
index c063061,1a7b51b..0000000
--- a/file
+++ b/file
@@@ -2,5 -2,5 +2,10 @@@ Line 1 Stuf
  Line 2 Stuff
  Line 3 Stuff
  Line 4 alternate stuff
++<<<<<<< HEAD
 +Line 5 stuff
 +Line 6 stuff
++=======
+ Line 5 alternate stuff
+ Line 6 alternate stuff
++>>>>>>> alternate

Inspecting Conflicts

  • >>>>>>>, =======, <<<<<<<は人間向けであってプログラム向けではない

    • コミットするまでにはちゃんと消そうね

git diff with conflicts

git diff
git diff --ours   # git diff HEAD と同義
git diff --theirs # git diff MERGE_HEAD と同義
git diff --base   # git diff $(git merge-base HEAD MERGE_HEAD) と同義
diff --cc file
index c063061,1a7b51b..0000000
--- a/file
+++ b/file
@@@ -2,5 -2,5 +2,10 @@@ Line 1 Stuf
  Line 2 Stuff
  Line 3 Stuff
  Line 4 alternate stuff
++<<<<<<< HEAD
 +Line 5 stuff
 +Line 6 stuff
++=======
+ Line 5 alternate stuff
+ Line 6 alternate stuff
++>>>>>>> alternate
* Unmerged path file
diff --git a/file b/file
index c063061..a5e02b0 100644
--- a/file
+++ b/file
@@ -2,5 +2,10 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
+<<<<<<< HEAD
 Line 5 stuff
 Line 6 stuff
+=======
+Line 5 alternate stuff
+Line 6 alternate stuff
+>>>>>>> alternate
* Unmerged path file
diff --git a/file b/file
index 1a7b51b..a5e02b0 100644
--- a/file
+++ b/file
@@ -2,5 +2,10 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
+<<<<<<< HEAD
+Line 5 stuff
+Line 6 stuff
+=======
 Line 5 alternate stuff
 Line 6 alternate stuff
+>>>>>>> alternate
* Unmerged path file
diff --git a/file b/file
index a3c4be1..a5e02b0 100644
--- a/file
+++ b/file
@@ -2,3 +2,10 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
+<<<<<<< HEAD
+Line 5 stuff
+Line 6 stuff
+=======
+Line 5 alternate stuff
+Line 6 alternate stuff
+>>>>>>> alternate

git log with conflicts

git log --merge --left-right -p
commit > 152d410b862f8f74966ed4b622a58704af67b51f
Author: Daiki Horiyama <xxxxxxxxx@gmail.com>
date:   Sun Nov 24 16:04:36 2019 +0900

    Add alternate line 5 and 6

diff --git a/file b/file
index a3c4be1..1a7b51b 100644
--- a/file
+++ b/file
@@ -2,3 +2,5 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
+Line 5 alternate stuff
+Line 6 alternate stuff

commit < bf387d7ce60288c102df6566b37d07d0679b99e4
Author: Daiki Horiyama <xxxxxxxxx@gmail.com>
date:   Sun Nov 24 15:57:00 2019 +0900

    Add line 5 and 6

diff --git a/file b/file
index a3c4be1..c063061 100644
--- a/file
+++ b/file
@@ -2,3 +2,5 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
+Line 5 stuff
+Line 6 stuff
  • oursとtheirsそれぞれについての情報が得られる

    • いつ
    • なぜ
    • どのように
    • 誰が
  • オプション解説

    • --merge

      • コンフリクトを生じているファイルのみ表示
    • --left-right

      • oursを<で、theirsを>で表す
    • -p

      • 歴史にコミット情報とpatchも添えるやつ
  • マージの苦痛を和らげるためには、小さなコミットをこまめにマージして

How Git Keeps Track of Conflicts

  • .git/MERGE_HEAD
  • .git/MERGE_MSG

    • マージコミットのデフォルトメッセージ
  • インデックス

    • base, ours, theirsの3つ保持する
    • git ls-files -uで3つ出てきたやつ
  • 作業ディレクトリ

    • >>>>>>>等のマーカー付きのファイルはインデックスではなく作業ディレクトリにある

      • だから--cachedなしのgit diffで差分を表示できる
  • git diffで、インデックス上の別バージョンのファイルの比較を行うことができる

    • 1: base
    • 2: ours
    • 3: theirs
git diff :2:file :3:file
diff --git a/file b/file
index c063061..1a7b51b 100644
--- a/file
+++ b/file
@@ -2,5 +2,5 @@ Line 1 Stuff
 Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
-Line 5 stuff
-Line 6 stuff
+Line 5 alternate stuff
+Line 6 alternate stuff
  • コンフリクト解消のコンテキストにおいて、git checkout --ours/--theirsで一方の変更を受け入れられる
cat file
Line 1 Stuff
Line 2 Stuff
Line 3 Stuff
Line 4 alternate stuff
<<<<<<< HEAD
Line 5 stuff
Line 6 stuff
=======
Line 5 alternate stuff
Line 6 alternate stuff
>>>>>>> alternate
git checkout --theirs file
cat file
Line 1 Stuff
Line 2 Stuff
Line 3 Stuff
Line 4 alternate stuff
Line 5 alternate stuff
Line 6 alternate stuff

Finishing Up a Conflict Resolution

git ls-files -s
100644 a3c4be15bb92e24db3cc4a2db0aa889148a93afe 1	file
100644 c06306161c8c2bd4a16da3f6a3e7e130b9fcecca 2	file
100644 1a7b51bee6459775eb9f30df455ea327fe0d99fe 3	file
100644 eaeeeba5973e46152dc758215c7e76dcecfd5f9b 0	other_file
  • SHA1とファイル名との間の数字は、ステージング番号

    • 1: base
    • 2: ours
    • 3: thries
    • 0: コンフリクトがないことを意味する
git add file
git ls-files -s
100644 1a7b51bee6459775eb9f30df455ea327fe0d99fe 0	file
100644 eaeeeba5973e46152dc758215c7e76dcecfd5f9b 0	other_file
  • コンフリクトを解消してgit add等すると0になる

    • >>>>>>>>とかを消し忘れぬよう
git commit
(コミットメッセージ編集)
git show
Merge branch 'alternate' into tmp

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


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch tmp
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#	modified:   file
#
commit fb21453bd655788150e16cc43c4479ebe0ef2649 (HEAD -> tmp)
Merge: bf387d7 152d410
Author: Daiki Horiyama <xxxxxxxxx@gmail.com>
date:   Sun Nov 24 17:22:49 2019 +0900

    Merge branch 'alternate' into tmp
  • 通常のコミットとマージコミットの違い

    • Merge: bf387d7 152d410

      • 親が2つ(以上)ある
    • デフォルトコミットメッセージにコンフリクトのあったファイルが記述されている

      • #でコメントアウトされている
      • マージ後に問題が発生したときはおそらくこのファイルが悪い
    • diffはマージ元ブランチとの差分のみ
git checkout master
git diff HEAD^
diff --git a/file b/file
index c063061..89e2b8f 100644
--- a/file
+++ b/file
@@ -3,4 +3,4 @@ Line 2 Stuff
 Line 3 Stuff
 Line 4 alternate stuff
 Line 5 stuff
-Line 6 stuff
+Line 6 alternate stuff

Aborting or Restarting a Merge

  • コミット前なら
git reset --hard HEAD
  • コミット直後なら
git reset --hard ORIG_HEAD
  • ORIG_HEAD: マージ直後にやっぱりやめる用のref
  • working directoryがdirtyな状態でmergeを始めていた場合、その変更分は失われるので注意
  • 【補】git merge --abortというのもある

英語

  • octothorpe

    • 「#」記号
  • disparty

    • 差異
  • gory detail

    • おぞましいまでの細部
  • mitigate the pain

    • 苦痛をやわらげる
  • botch

    • しくじる