【Git】初心者向け Gitをつかってみる その２（git branch と git merge）

# はじめに
[Git Advent Calendar 2023 2日目](https://qiita.com/advent-calendar/2023/git)の投稿です。よろしくおねがいします。
[前回](https://qiita.com/arubaito/private/607ebc3e6f87b50d5a68)の記事では`git init`から`git commit`までやりました。今回も初心者向けに、開発シーンでよく使うブランチを切ってマージするところまでGitを使ってみようと思います。
（今回も自分向けの頭の整理も兼ねてます:writing_hand:）

# 準備
- 「git/sample」リポジトリを用意
- リポジトリに「hello」と書き込んだ「sample.txt」をコミットしておく
（前回の内容を進めていると上記の内容になってます）

`git log`した状態（`git log`はコミットの履歴を確認するコマンドです）
```batch
 ~/Desktop/git/sample (master)
$ git log
commit 9db45bbb0a8487fde3a648f5eb507768613f8018 (HEAD -> master)
Author: hogehoge <fugafuga>
Date:   Wed Nov 8 10:51:13 2023 +0900

    はじめてのコミット
```

# ブランチとは
ブランチとは簡単に言うと**ある開発ラインから別の開発ラインへと分岐するための方法**です。ここで、Gitがどのようにファイルを管理するかについて説明しつつ、ブランチが何なのか説明してみます。
（イメージがついてると以降の理解の助けになるかなと思って書きます。）

Gitは以下の4種類のオブジェクトを使ってファイルを管理しています。
1. **ブロブ**
    - ある時点のファイル。
1. **ツリー**
    - 1階層分のフォルダ情報。
    - ブロブとツリーを参照する。
1. **コミット**
    - 変更のメタデータ（ファイルの作成者とか、日付とか）。
    - ツリーとコミットを参照する。
1. **タグ**
    - 特定のオブジェクトに対して人が読みやすくなるようにした名前。
    - コミットを参照する。

オブジェクトはすべてSHA1ハッシュ（40桁の16進数。例えば「a564b5f376a8cacf10b87369d122743bbd82fc19」みたいな値）の一意な名前で管理されます。

例えば、リポジトリの直下に「hello world」と書かれたファイルAと、「bye world」と書かれたファイルBを配置して、コミットしたときのオブジェクト間の関係を図示してみると以下のようになります。

- リポジトリの直下にファイルを2つ配置してコミットした場合
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/8d77d9a8-bee8-d336-51b1-53ecbcc78e13.png)

コミットがツリーを、ツリーがブロブを参照してリポジトリの中身を表現しています。
つまり、コミットを参照することである時点のリポジトリの中身を知れます。

ここで、**リポジトリの最新のコミットを参照するのがブランチ**です。
- ブランチのイメージ図
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/5e4d5028-83c5-d7ab-3cd9-c5f518fe841a.png)

（おいおい、ちょっと待てよ、ブランチは開発ラインを分岐するための方法ちゃうんかい、どこがやねん:sweat_smile:と思った方、もう少し辛抱して下さい:pray:）

ブランチは常に最新のコミットを参照し続けます。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/53edd3d8-33ad-88ec-c072-699f7ddd67cc.png)

ブランチを複数作るとどうなるでしょうか。
同じコミットを参照するように新たに`another`ブランチを作ると以下の図のようになります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/850eefa7-e052-b700-ab15-1bd8dbbf1523.png)

今は同じコミットを参照している状態ですが、ここで`another`ブランチに対して作業をしてコミットをしてみると以下の図のようになります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/5c614974-b392-019a-5199-7179bf1cc80f.png)


ブランチは最新のコミットを参照し続けるので、`another`ブランチは**最新のコミットを参照**するようになり、`master`ブランチは**１つ前のコミットを参照したまま**の状態になります。

このようにして、`master`ブランチはある時点のリポジトリの状態を表現することを保ったまま、`another`ブランチでは`another`ブランチ専用で作業を続けることができます(これを指して冒頭でブランチは開発ラインを分ける方法と言いました)。

リポジトリのデフォルトのブランチは`master`という名前が付けられてて、ほとんどの開発ではこの`master`を最も信頼できるソースコードの履歴として使っていると思います。
そして、他のブランチは、開発用だったりバグ修正用だったり様々な状況の開発ラインとして利用して、最後に`master`に集める、、なんてことをすると思います。

ブランチを作る（ある開発ラインから別の開発ラインへと分岐する）理由は様々あります。
- 作業者ごとに開発できるようにする
- ソースコードのバージョンを分ける
…など

色々書きましたが、実務ではとりあえずはブランチを作ることとは**自分だけの作業領域ができる**理解でいいと思います。

# git branch
では、実際にブランチを作ってみましょう。
準備として下記のコマンドを打って3回コミットしておきます（コミット何個かあった方がおもしろそうなので）。
```batch
説明：文字を「sample.txt」に書き込んでコミット
~/Desktop/git/sample (master)
$ echo "hello world" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"２回目のコミット"
[master 437e558] ２回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明：文字を「sample.txt」に書き込んでコミット
~/Desktop/git/sample (master)
$ echo "hello hello world" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"３回目のコミット"
[master c4a4731] ３回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明：文字を「sample.txt」に書き込んでコミット
~/Desktop/git/sample (master)
$ echo "hello hello world release" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"１回目のリリース"
[master 630817a] １回目のリリース
 1 file changed, 1 insertion(+), 1 deletion(-)
```
説明していないコマンドとオプションをいくつか使っているので説明しておきます。
- `echo`はGitのコマンドではなくコンソールに文字を表示するLinuxのコマンドです。表示した文字は`>`でファイルに上書きで転送することができます。
- `git add .`の`.`は作業フォルダで編集があった**すべての**ファイルがインデックスに登録されます。ファイル名打つのが面倒な時かつすべてステージしてよい時に使ってます。
※ここで改行コードのwarningが出るかもしれませんが、いったんは気にしなくていいです。
- `git commit -m <msg>`で指定された\<msg> をコミットメッセージとして使用します。エディタでの入力を省略できます。

こうすることで以下のコミットの履歴ができます。この図は**コミットツリー**と呼ばれます。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/57ebbade-664d-9d3e-9986-4f21b32808f1.png)

:arrow_down:図の説明です。
- 丸がコミットです。
- 冒頭の図では、コミットの参照としてツリーが、ツリーの参照としてブロブがついていました。が、コミットが分かればある時点のリポジトリの状態がわかるのでツリーやブロブは省略して書いてます。
- また、参照の向きを表す矢印はどちらの方向か推測できるので省略してます。

上の図はコマンドでも確認することができて、確認するには`git log --graph`を使います。
```batch
~/Desktop/git/sample (master)
$ git log --graph
* commit 5b09212c8a6602f39c9abe12266eff6c3f94fd35 (HEAD -> master)
| Author: hogehoge <fugafuga>
| Date:   Fri Nov 10 09:52:31 2023 +0900
|
|     １回目のリリース
|
* commit 9baeecead59dcc40b594db13a9cfc0189f9545cf
| Author: hogehoge <fugafuga>
| Date:   Fri Nov 10 09:51:52 2023 +0900
|
|     ３回目のコミット
|
* commit d1d0866818415586c47819f032e80b9ac2a3dff4
| Author: hogehoge <fugafuga>
| Date:   Fri Nov 10 09:50:15 2023 +0900
|
|     ２回目のコミット
|
* commit 677b7f568e7a1563213e0de75677949449108703
  Author: hogehoge <fugafuga>
  Date:   Fri Nov 10 09:48:03 2023 +0900

      はじめてのコミット
```
新しいコミットから古いコミットへさかのぼる形です。

では、さっそくブランチを作りましょう。
`git branch <branchname>`で**現在のコミットから分岐して新たなブランチ**ができます。また、ブランチの一覧を確認するには`git branch`です。

```batch
~/Desktop/git/sample (master)
$ git branch another

~/Desktop/git/sample (master)
$ git branch
  another
* master
```
`git branch`で表示した際に`*`がついているブランチが現在作業しているブランチです（ここでは**カレントブランチ**と言います）。
- 今の状況
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/a1036d66-bc29-bbff-60ad-fd0370990ad0.png)


仮にこの後コミットをすると、カレントブランチである`master`ブランチにコミットがくっつきます。言い換えると、次のコミットは`master`の参照先になります。

`another`ブランチで作業を続けましょう。**ブランチを切り替える**には`git checkout <branchname>`を使います。
```batch
~/Desktop/git/sample (master)
$ git checkout another
Switched to branch 'another'

~/Desktop/git/sample (another)
$ git branch
* another
  master
```
カレントブランチをあらわす`*`が`another`ブランチに切り替わりました。

ちなみに、少し話がそれますがカレントブランチはGitでは`HEAD`で表示されます。
少しだけ`HEAD`の話をします。`git log --oneline`をしてみましょう（`--oneline`は各コミットのコミットメッセージの1行目だけの`git log`が表示されます）。
```batch
~/Desktop/git/sample (another)
$ git log --oneline
5b09212 (HEAD -> another, master) １回目のリリース
9baeece ３回目のコミット
d1d0866 ２回目のコミット
677b7f5 はじめてのコミット
```
`(HEAD -> another, master)`がログに付与されています。この`->`の意味は、「カレントブランチ(`HEAD`)は`another`ブランチを参照している」という意味になります。
`HEAD`も参照なので、図であらわすと以下のようになります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/185bf60a-8aef-4a29-c9fa-b03c1a47361d.png)

`git checkout <branchname>`で指定のブランチに`HEAD`の参照が切り替わるわけです。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/08b1bf6b-e8fa-cc51-41a5-90eff1d7b899.png)


、、すみません、話が脱線しました。
さて、話を戻します。繰り返しの図になりますが、今は以下の状況でした。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/185bf60a-8aef-4a29-c9fa-b03c1a47361d.png)


ここで再度コミットを何度かしてみるとどうなるでしょうか。

```batch
説明：「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (another)
$ echo "another hello" > sample.txt

~/Desktop/git/sample (another)
$ git add .

~/Desktop/git/sample (another)
$ git commit -m""anotherブランチで１回目のコミット
[another ec679ef] anotherブランチで１回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明：「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (another)
$ echo "another hello world" > sample.txt

~/Desktop/git/sample (another)
$ git add .

~/Desktop/git/sample (another)
$ git commit -m"anotherブランチで２回目のコミット"
[another 96a71fa] anotherブランチで２回目のコミット
 1 file changed, 1 insertion(+), 1 deletion(-)
```

- `git log`
```batch
~/Desktop/git/sample (another)
$ git log
commit 96a71fa7be0f180a0118ef8d090e9d51bb3d37df (HEAD -> another)
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:59:48 2023 +0900

    anotherブランチで２回目のコミット

commit ec679efe389eb8167f70e9739e1959d27eff4a71
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:58:43 2023 +0900

    anotherブランチで１回目のコミット

commit 5b09212c8a6602f39c9abe12266eff6c3f94fd35 (master)
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:52:31 2023 +0900

    １回目のリリース

commit 9baeecead59dcc40b594db13a9cfc0189f9545cf
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:51:52 2023 +0900

    ３回目のコミット

commit d1d0866818415586c47819f032e80b9ac2a3dff4
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:50:15 2023 +0900

    ２回目のコミット

commit 677b7f568e7a1563213e0de75677949449108703
Author: hogehoge <fugafuga>
Date:   Fri Nov 10 09:48:03 2023 +0900

    はじめてのコミット
```
- 今の状況イメージ図
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/7ffae73d-f986-f462-a28b-a98f49c33c37.png)

このようにして、`master`ブランチでは最新のコミットがハッシュ値`5b0921`を指し示し、`another`ブランチでは開発を進めてハッシュ値`96a71f`までコミットをすることができました。

ブランチを作って開発を進めることが出来ましたね。


# git merge
次に、`another`ブランチのコミットを`master`ブランチに取り込んでみましょう。**分岐したコミットの履歴を統合させること**をマージと言います。
`git merge <branchname>`でカレントブランチに`branchname`の履歴を取り込めます。カレントブランチ**へ**取り込むことに注意です。
マージされたことが分かるようにここでは`--no-ff`オプションを使ってマージします（後述します）。
```batch
~/Desktop/git/sample (another)
$ git checkout master
Switched to branch 'master'

~/Desktop/git/sample (master)
$ git merge --no-ff another
Merge made by the 'recursive' strategy.
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
```
コミットメッセージを入力するエディタが表示されるので任意のコメントを入力してコミットしてください（ここではデフォルトの内容でいきます）。

`git log --oneline --graph`をするとマージの状況が分かります。
```batch
~/Desktop/git/sample (master)
$ git log --oneline --graph
*   3124c16 (HEAD -> master) Merge branch 'another'
|\
| * 96a71fa (another) anotherブランチで２回目のコミット
| * ec679ef anotherブランチで１回目のコミット
|/
* 5b09212 １回目のリリース
* 9baeece ３回目のコミット
* d1d0866 ２回目のコミット
* 677b7f5 はじめてのコミット
```
`master`ブランチ上で`another`ブランチを取り込む操作をしたので、`master`ブランチに`another`ブランチの内容を取り込むコミットが１つ生成されてます。
- イメージ図（`git log`と一緒ですが、、）
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/2d2d04ff-b896-f8be-554a-5996ed2eccf0.png)



ファイルの内容を確認すると、`another`ブランチの内容が反映されていることが分かります（コミットID`96a71fa`の内容です）。
```batch
~/Desktop/git/sample (master)
$ cat sample.txt
another hello world
```
※`cat`はファイルの中身を確認したいときに利用されるLinuxのコマンドです。

ここで、マージするときにつけた`--no-ff`オプションについて説明しておきます。
Gitはマージするときにまず、**縮退マージ**と呼ばれる以下の２つのマージを試みます。
- **Already up-to-date**
    - マージ**する**側のブランチ（ここでいうと`master`）に、マージ**される**側のブランチ（ここでいうと`another`）からの**コミットがすべて含まれている場合**にGitから言われる。マージする内容がなく、コミットは追加されない。
- **Fast-forward**
    - マージ**される**側のブランチ（`another`）に、マージ**する**側のブランチ（`master`）の**コミットがすべて含まれている場合**におこなわれる。この際は、マージする側のブランチにマージされる側のコミットを付け足して、カレントブランチ（`HEAD`）は付け足した最新のコミットに移動する。また、マージのコミットが作られない。

今回は↑に書いた、Fast-forwardに当てはまります（`another`ブランチが`master`ブランチのコミットをすべて持っている状態）。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/ebf3791a-cd9e-fc64-d071-44e191b9b00a.png)
そのため、オプションをつけずに（`--no-ff`をつけずに）`git merge another`を行った場合は、Fast-forwardが行われて`master`ブランチに`another`コミットで付け足した２つのコミットがくっつくだけの処理となります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/d5ff56a6-074c-fd60-3ff6-c762184b3d9f.png)


`--no-ff`とはFast-forwardせずにマージせよ、というオプションになります。
マージが行われたことが分かりにくいので今回は`--no-ff`をつけてマージコミットを作りました。

## 競合
次に、２つのブランチで同一箇所を修正してマージしてみましょう。
```batch
説明：masterブランチ上で「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (master)
$ echo "master branch hello" > sample.txt

~/Desktop/git/sample (master)
$ git add .

~/Desktop/git/sample (master)
$ git commit -m"masterブランチでコミット"
[master 1cff7a9] masterブランチでコミット
 1 file changed, 1 insertion(+), 1 deletion(-)

説明：anotherブランチ上で「sample.txt」の中身を更新してコミット
~/Desktop/git/sample (master)
$ git checkout another
Switched to branch 'another'

~/Desktop/git/sample (another)
$ echo "another branch hello" > sample.txt

~/Desktop/git/sample (another)
$ git add .

~/Desktop/git/sample (another)
$ git commit -m"anotherブランチでコミット"
[another 9a8b34b] anotherブランチでコミット
 1 file changed, 1 insertion(+), 1 deletion(-)
```
`master`ブランチと`another`ブランチで同じファイルに修正を行いました。
コミットツリーは以下のようになっています。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526202/35ac1eb7-b251-b82a-5ef2-311e6a7a3463.png)


`git show-branch`コマンドを使ってみます。
これはブランチが分岐してから現在までのコミットを表示するコマンドです。各ブランチのコミットをさくっと知りたいときに便利です。
```batch
~/Desktop/git/sample (another)
$ git show-branch
* [another] anotherブランチでコミット
 ! [master] masterブランチでコミット
--
*  [another] anotherブランチでコミット
 + [master] masterブランチでコミット
*+ [another^] anotherブランチで２回目のコミット
```
`--`の上が各ブランチの最新のコミット、`--`の下が分岐後にブランチごとに何のコミットがあったかおおよそ時系列ごとに表示しています。すべてのブランチのログを見る`git log -all`の結果と一緒にみることで、先ほどマージした後に`master`と`another`でそれぞれコミットがおこなわれたことが分かります。

```batch
~/Desktop/git/sample (another)
$ git log --oneline --all
21085b7 (HEAD -> another) anotherブランチでコミット
1cff7a9 (master) masterブランチでコミット
3124c16 Merge branch 'another'　←マージコミット
96a71fa anotherブランチで２回目のコミット
ec679ef anotherブランチで１回目のコミット
5b09212 １回目のリリース
9baeece ３回目のコミット
d1d0866 ２回目のコミット
677b7f5 はじめてのコミット
```


それでは、`master`ブランチに`another`ブランチをマージしてみましょう。
```batch
~/Desktop/git/sample (another)
$ git checkout master
Switched to branch 'master'

~/Desktop/git/sample (master)
$ git merge --no-ff another
Auto-merging sample.txt
CONFLICT (content): Merge conflict in sample.txt
Automatic merge failed; fix conflicts and then commit the result.

~/Desktop/git/sample (master|MERGING)
$
```
メッセージを日本語に訳すと「競合が発生してマージは失敗したよ。競合を解消して結果をコミットしてね。」とのことです。
先ほどのマージでは`another`ブランチのみを修正して`master`ブランチとマージしたので、競合が発生せず自動的にマージされました。しかし、今回は双方のブランチで修正をおこなっており、自動的にマージができず**競合**がおこっている状態です。
この場合は**人間が解消して手動でマージコミットを作ってあげる**必要があります。
また、Git Bushでは`(master|MERGING)`のようにブランチ名の後ろにマージ中であることが表示されます。

私はこの競合が怖くてマージ操作ができなかったので、先に競合した際にマージ前の状況に戻る方法から書いておきます。マージ前の状況に戻るには`git merge --abort`です。
```batch
~/Desktop/git/sample (master|MERGING)
$ git merge --abort

~/Desktop/git/sample (master)
```
:::note warn
注意点
ちなみに、マージは作業フォルダで作業されるので、マージ前に作業フォルダで作業中のファイルが無いようにした方がいいです。`git merge --abort`が困難になる可能性があります。
:::

では、競合を解消してみましょう。どこで競合が起きたかはGitがファイルに直接書いています（気になる方は「sample.txt」を開いてみてください）。
`git diff`コマンドでどこに競合が発生したか確認してみましょう。
```diff
~/Desktop/git/sample (master|MERGING)
$ git diff
diff --cc sample.txt
index 81f4d78,2eb1448..0000000
--- a/sample.txt
+++ b/sample.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD
 +master branch hello
++=======
+ another branch hello
++>>>>>>> another
```

マージする側（`master`）の変更が`<<<<<<<`と`=======`の間、マージされる側（`another`）の変更が`=======`と`>>>>>>>`の間に書いてあります。競合の解消は人間がどうしたいか決めてファイルを編集します。
ここでは`another`の方を取り込むことにしましょう。今回は面倒なので、`echo`コマンドで上書きます（実際は、修正者にどういう意図で編集したか確認したりしてよく考えてやりましょう）。
手動でファイルを修正して`git add`と`git commit`でマージコミットを作るとマージが完了します。
```batch
説明：競合が発生しているファイルを上書きで修正してコミット
~/Desktop/git/sample (master|MERGING)
$ echo " another branch hello" > sample.txt

~/Desktop/git/sample (master|MERGING)
$ git add .

~/Desktop/git/sample (master|MERGING)
$ git commit
[master a97dcd5] Merge branch 'another'
```

マージを２回おこなったので`git log --oneline --graph`は以下のようになります。
```batch
~/Desktop/git/sample (master)
$ git log --oneline --graph
*   a97dcd5 (HEAD -> master) Merge branch 'another'
|\
| * 21085b7 (another) anotherブランチでコミット
* | 1cff7a9 masterブランチでコミット
* |   3124c16 Merge branch 'another'
|\ \
| |/
| * 96a71fa anotherブランチで２回目のコミット
| * ec679ef anotherブランチで１回目のコミット
|/
* 5b09212 １回目のリリース
* 9baeece ３回目のコミット
* d1d0866 ２回目のコミット
* 677b7f5 はじめてのコミット
```

また、マージの完了後にマージをした内容を破棄したい場合は`git reset --hard ORIG_HEAD`するとマージ前の状況に戻れます。


## 補足
ところで、マージをした時に`Merge made by the 'recursive' strategy.`とでました。
```batch
~/Desktop/git/sample (master)
$ git merge --no-ff another
Merge made by the 'recursive' strategy.
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
```
recursiveマージについて、簡単に補足しておきます。
マージの際はマージコミットを作り出すためのマージのアルゴリズムを選択できます（これをマージ戦略といいます）。
recursiveマージはマージ戦略の1つです。同時に2つのブランチのみ扱えて、すべてのマージ起点のもとになった共通的な位置に一時的なマージを作り出して、そこをマージ基点として通常の3wayマージをおこなう戦略です。

### ところで3wayマージって？
以下の説明を、簡略化して和訳してみます（参照してくださいでもいいんですけど自分のために和訳してメモとして載せてます）。

https://blog.git-init.com/the-magic-of-3-way-merge/

#### マージのジレンマ
2人以上の開発者が同じファイルにそれぞれ変更を加え、あとでそのバージョンをマージしようとすると競合が起こる可能性があります。
しばらくの間、バージョン管理システムを忘れて、具体的なケースを考えてみましょう。2人の開発者、太郎と花子が同じ既存の「index.html」ファイルに変更を加え、最終的にそれぞれ1つの新たなリビジョンを作成したとします。この2つのリビジョンの変更を失うことなくマージするにはどうすればよいでしょうか？

```（太郎側）index.html
<html>
    <head>
        <title>サンプル</title>
    </head>
    <body>
        <ul class="animals">
            <li>Mouse</li>
            <li>Cat</li>
            <li>Horse</li>
        </ul>
    </body>
</html>
```

```（花子側）index.html
<html>
    <head>
        <title>サンプル</title>
    </head>
    <body>
        <ul class="animals">
            <li>Moose</li>
            <li>Cat</li>
            <li>Dog</li>
        </ul>
    </body>
</html>
```

両者とも`<ul>`ブロックの動物リストを編集していることがわかりますね。もし、この2つのリビジョンから最終的なマージのバージョンを作ることになった場合、あなたならどうするでしょうか？
以下のマージのジレンマが発生します。
1. 太郎が`Moose`を`Mouse`に変えたのでしょうか？それとも、花子が`Mouse`を`Moose`に変えたのでしょうか？
2. `Horse`と`Dog`はそれぞれの開発者が加えたのでしょうか？それとも、2人の開発者のどちらかもしくはどちらもがリストの最後の項目を以前の項目から更新したのでしょうか？

この2つのリビジョンの情報では、上記を知ることは不可能です。このシナリオは、2つのリビジョンを静的に比較して手動でマージさせる2wayマージと呼ばれます。

#### 3wayマージ
上のように各開発者がそれぞれどのような変更をおこなったか手動でたどる代わりに、3wayマージがあります。最新のバージョン管理システムでは「最も近い共通の先祖」（マージ基点と言います）を自動的に見つけることができます。このマージ基点も合わせた3つのリビジョンを使って最終的なマージのリビジョンを作成することを「3wayマージ」と呼びます。
例えば、マージ基点が以下だったとしましょう。

```（マージ基点）index.html
<html>
    <head>
        <title>サンプル</title>
    </head>
    <body>
        <ul class="animals">
            <li>Moose</li>
            <li>Cat</li>
        </ul>
    </body>
</html>
```
マージ基点を特定すれば、望ましい最終バージョンを作成するために何を変更したか見つけ出すのは簡単です。
マージ基点と太郎と花子のリビジョンの比較を見ると、マージ後の最終バージョンがどうあるべきかを理解するのがずっと簡単になります。
マージのジレンマの「1. 太郎が`Moose`を`Mouse`に変えたのでしょうか？それとも、花子が`Mouse`を`Moose`に変えたのでしょうか？」については、太郎が変更をしたことが明らかになります。
また、「2. `Horse`と`Dog`はそれぞれの開発者が加えたのでしょうか？それとも、2人の開発者のどちらかもしくはどちらもがリストの最後の項目を以前の項目から更新したのでしょうか？」については2人の開発者それぞれが`Horse`と`Dog`を追加したことが分かります。
これを踏まえると、マージコミットとして最終的なリビジョンを作成することができます。

#### 要するに
3wayは3つのコミットを表しています。

①マージ元ブランチのポインタに該当するコミット
②マージ先ブランチのポインタに該当するコミット
③①、②の共通祖先となるコミット

「①と③の差分」と、「②と③の差分」をマージ元ブランチに取り込む方法を「3wayマージ」と言います。（↓の説明をお借りしました。）

https://kz-ike.hatenablog.com/entry/2021/01/04/175259

:::note info
ちなみに、今回使用したバージョン2.22では`recursive`がデフォルト戦略として選択されますが、[最新のバージョン](https://git-scm.com/docs/merge-strategies)のデフォルト戦略は`ort`というやつでした。
:::

# まとめ
今回は`git branch`をしてブランチを2本用意して、`git merge`でブランチ間をマージしてみました。
間違いがあればコメントでご指摘いただけるとうれしいです:pray:

# 参考
https://www.oreilly.co.jp/books/9784873114408/