GitHubでのマージ3種類もあるの？どう使い分けるの？を解説してみた

この記事は[Ateam LifeDesign Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ateam-life-design)の12日目記事です。

# はじめに
先日チーム内でこんな出来事がありました。

Sさん「PRたくさん作ってあるから、リリース用のPRにまとめておいて〜」
自分「はい！」

〜〜数時間後〜〜

自分「Sさん、リリース用のPRにまとめました！」
Sさん「これ、`Squash and merge`してない？ `Rebase and merge`した方がコミット履歴が綺麗に残るから次は気をつけてね〜」
自分「、、、すいませんでした🙇」

`Squash and merge`をしたことで、コミットが1つにまとまってしまい、コミットは綺麗になったものの、履歴がどのPRでどんな修正をしたのか、追いづらい状態になってしまいました。

反省の意も込めて、勉強した内容をアウトプットいたします。

# GitHubでのマージの種類
上記でもチラッと記載していますが、GitHubでのマージの種類は以下3種類あります。

- `Create a merge commit`
- `Squash and merge`
- `Rebase and merge`

まずは、それぞれを言語化していきます。

## Create a merge commit
- 動作でいうと、`git merge --no-ff`になる
- マージ先のブランチに、新たにマージコミットが作成される

## Squash and merge
- 動作でいうと、`git merge --squash`になる
- マージ先のブランチに、新たにマージコミットが作成される
    - このマージコミットに、作業した複数のコミットが1つにまとめられる

## Rebase and merge
- マージ先のブランチに、マージコミットは作成されない
- `rebase`を行っているので、マージ先のコミットから、マージするブランチのコミットが続くようになる

次に、実際に操作した際の挙動を見てみようと思います。

# 挙動確認
## Create a merge commit
### 前提
以下のようになっているコミットに対して、`Create a merge commit`を行います。

![スクリーンショット 2023-12-05 20.51.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849898/2449436b-28dd-4168-a107-a14263f79218.png)

### 確認
`test-1`というブランチを切って、そこで以下のコミットを行いました。

- `feat: test 1`
- `feat: test 2`

PRを作成して、`Create a merge commit`を行ったところ、以下のようになりました。

![Group 1 (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849898/4fe59563-a424-b2cf-1d64-2b5f9073f66a.png)

作業したコミットと同時に、マージコミットも作成されていました。


## Squash and merge
### 前提
上記の続きに、`Squash and merge`を行います。

### 確認
`test-2`というブランチを切って、そこで以下のコミットを行いました。

- `feat: test 3`
- `feat: test 4`

PRを作成して、`Squash and merge`を行ったところ、以下のようになりました。

![Group 2 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849898/65f88b2f-8fa3-54c5-6689-ff665772fada.png)

マージコミットは作成されますが、作業したコミットもマージコミットに含められています。


## Rebase and merge
### 前提
上記の続きに、`Rebase and merge`を行います。

### 確認
`test-3`というブランチを切って、そこで以下のコミットを行いました。

- `feat: test 5`
- `feat: test 6`

PRを作成して、`Rebase and merge`を行ったところ、以下のようになりました。

![Group 1 (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849898/5e59ef87-9535-3c7a-d4f7-c9a2c469ebd2.png)

マージコミットは作成されず、続きとして反映されています。

# 使い方
**結論、チームの開発スタイルや時と場合によって異なるので、一概にこうとは言えないと思います。**

個人的には、`Create a merge commit`はマージコミットができてしまい、余分なコミットになってしまうので、あまり使うイメージはないです。
`Squash and merge`と`Rebase and merge`を場面によって、使い分けると思います。

以下に、実際にあった例を記載いたします。

## 例
`main`ブランチから、作業ブランチとして、A・Bブランチを作成します。
その2つのブランチをまとめた、リリース用のCブランチも作成します。
最後に、リリース用のCブランチから作成したPRを、`main`にマージします。

※ Bブランチは、ある理由でマージ後にリバートする予定です。

上記のような条件の場合、以下のような形になると思います。

- 作業ブランチA・Bを、リリース用Cブランチにマージする際は、それぞれ`Squash and merge`
- リリース用Cブランチを、`main`にマージする際は、`Rebase and merge`

図にすると、以下のようになります。

```mermaid
stateDiagram-v2
    Aブランチ（作業） --> Cブランチ（リリース用）: Squash and merge
    Bブランチ（作業） --> Cブランチ（リリース用）: Squash and merge
    Cブランチ（リリース用） --> main: Rebase and merge
```

Bブランチはリバート前提なので、リリース用Cブランチには、コミットをまとめる`Squash and merge`をして、`main`へのマージは、`Rebase and merge`にすると、後でリバートがしやすくなります。
`main`へのマージを、`Squash and merge`してしまうと、Aブランチとコミットがまとまってしまうので、後でリバートがしにくくなってしまいます。

このように、時と場合によって、マージの方法を使い分ける必要があります。


# まとめ
ここまでで、GitHubのマージ3種類と、どう使い分けるかを説明いたしました。
使い分けるかに関しては、本当に開発によると思うので、上記の例などを参考にその時に応じて、マージを使い分けていただけたらと思います。

最後までご覧いただき、ありがとうございました。

# 参考文献
https://qiita.com/ko-he-8/items/94e872f2154829c868df

https://docs.github.com/ja/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges

https://www.sunapro.com/github%E3%81%A7%E3%81%AE3%E7%A8%AE%E9%A1%9E%E3%81%AE%E3%83%9E%E3%83%BC%E3%82%B8%E3%81%AE%E4%BB%95%E6%96%B9/