git rebase と git merge の使い方と使用シーンを紹介します！

## `git merge` の紹介

マージの基本的な構文は：`git merge <branch>` です。

マージ操作は、複数のブランチの開発履歴を一つに統合し、全てのブランチのコミットを保持します。そして、マージ操作の完了を示す新しいマージコミット（merge commit）が追加されます。

ここで、以下のような `fix` と `master` ブランチ、およびコミット履歴があるとします：

```
        A --- B --- C  fix
       /
D --- E --- F --- G  master
```

この時点で、`fix` ブランチの最新のコードを `master` ブランチにマージしたいと考えています。

`master` ブランチで `git merge fix` コマンドを実行することができます。

これにより、`fix` ブランチのコミット内容（現在のブランチから分岐して以来の全てのコミット）が `master` ブランチにマージされます。

その結果、コミット履歴は次のようになります：

```
        A --- B --- C  fix
       /              \
D --- E --- F --- G --- H  master
```

`master` ブランチのコミット履歴には、新しいマージコミット H が追加されます。

H のコミットには、`fix` ブランチの全ての A、B、C のコミットが含まれます。

### `git merge` を避けるべき場面

`git merge` を使うと、ブランチのすべてのコミット履歴が保存され、さらに新しいマージコミットが追加されます。**これは公共のブランチでは良いマージ戦略です**。

しかし、**プライベートブランチや共有されていないブランチでこれを行うと、問題が生じやすいです**。

次のような `feature` と `master` ブランチ、およびコミット履歴があるとします：

```
D --- E --- F --- G  master
        \
    	  A --- B  feature
```

`feature` ブランチを開発している間に、`master` ブランチが二度更新されました。

`feature` ブランチのコードを `develop` ブランチにマージしてテストする前に、コードの整合性を保つために `master` ブランチの最新のコードを `feature` ブランチにマージする必要があります。

この時点で `git merge` を続けると、コミット履歴は次のようになります：

```
D --- E --- F --- G  master
        \           \
    	  A --- B --- H  feature
```

つまり、`feature` ブランチのコミット履歴は次のようになります：

```
A --- B --- H（F と G が含まれている）  feature
```

H のコミットには `master` ブランチの最新のコミット、すなわち F と G が含まれます。

コードのマージが成功した後、`feature` ブランチのコードを `git merge` を使って公共のブランチ `develop` にマージします。

コミット履歴は次のようになります：

```
D --- E --- F --- G  master
        \           \
    	  A --- B --- H  feature
                        \
X --- Y --- Z ----------- M  develop
```

`develop` ブランチには M コミットが追加され、M コミットには `feature` ブランチのすべてのコミット（A、B、H）が含まれ、H コミットには `master` ブランチの F と G が含まれます。

この例では、コミット数がわずか数回でも、2 回のマージ後に `feature` ブランチと `develop` ブランチの Git コミット履歴がこれほど複雑になります。

コミット数が増え、マージ回数が増えると、コードレビューやデバッグやコードの履歴追跡が非常に困難になります。

私が経験した中で、コミット数が 200 を超える場合の MR がありましたが、これはメインブランチの最新コードをマージした結果です。

したがって、余分なコミット履歴を削除し、コミット履歴をクリーンに保つことが重要です。

これにより、コードのレビューが楽になり、コードのデバッグやコードの履歴追跡も理解しやすくなります。

## `git rebase` の紹介

リベースの基本的な構文は： `git rebase <branch>` です。

コードの統合を行う際、不要なコミット履歴を取り除き、履歴をシンプルで直線的に保つには、`git rebase` を使用する必要があります。

`git merge` とは異なり、`git rebase` はマージコミットを作成せず、現在のブランチのコミットを基準ブランチの最新コミットに再適用します。これにより、現在のブランチの履歴が基準ブランチの最新コミットから直接派生したかのように見え、履歴が直線的でシンプルになります。

### 上記の `git merge` が適さない場面の問題解決

上記と同じ、以下のような `feature` と `master` ブランチ、およびコミット履歴があるとします：

```
D --- E --- F --- G  master
        \
    	  A --- B  feature
```

`feature` ブランチを開発している間に、`master` ブランチが二度更新されました。

`feature` ブランチのコードを `develop` ブランチにマージしてテストする前に、コードの一貫性を保つために `master` ブランチの最新コードを `feature` ブランチにマージする必要があります。

この時点で `git merge` ではなく `git rebase` を使用すると、コミット履歴は次のようになります：

```
D --- E --- F --- G  master
                    \
     	             A --- B  feature
```

つまり、`feature` ブランチのコミット履歴は次のようになります：

```
A --- B  feature
```

不要な H、F、G のコミットが含まれていません。

リベースが成功した後、`feature` ブランチのコードを `git merge` を使用して公共のブランチ `develop` にマージします。

コミット履歴は次のようになります：

```
D --- E --- F --- G  master
                   \
    	            A --- B  feature
                            \
X --- Y --- Z --------------- M  develop
```

`develop` ブランチには M コミットが追加され、M コミットには `feature` ブランチの全てのコミット（A と B）が含まれます。

以前の `git merge` を使用した場合と比較して、`develop` ブランチに追加された M コミットには `feature` ブランチの全てのコミット（A、B、H）が含まれ、H には `master` ブランチの F と G のコミットが含まれています。

現在、M コミットには `feature` ブランチのコミット（A と B）のみが含まれています。

上記の例から、プライベートブランチや共有されていないブランチで `git rebase` を使用して基準ブランチの最新コードを取り込むことで、次のような利点があります：

1. コミット履歴を直線的で分かりやすく保つ

   これにより、プロジェクトのコミット履歴が読みやすく理解しやすくなり、コードレビューが容易になります。

   また、デバッグやコードの遡りにおいても、コードの進化過程が理解しやすくなります。

2. 不要なマージコミットを避ける

3. 早期にコンフリクトを解決し、協力効率を向上させる

   `git rebase` によって早期にコンフリクトを解決することで、最終的なマージ時の複雑さを軽減できます。

   さらに、基準ブランチの最新の変更を定期的にプライベートブランチに適用することで、常に最新のコードベースに基づいて作業を行い、チームメンバー間のコードコンフリクトを減らすことができます。

### コンフリクトがない場合

`git rebase <branch>` を実行した後、コードにコンフリクトがなければ、そのままリベースが完了しています。

その後は `git push -f` を使ってリモートブランチにコードをプッシュするだけです。

> なぜ `git push -f` を使う必要があるのでしょうか？
>
> `git rebase` を実行すると、Git は各コミットに新しいコミットオブジェクトを作成します。これらの新しいコミットオブジェクトは、内容が同じであっても元のコミットとは異なります。
>
> そのため、ローカルブランチのコミット履歴がリモートブランチの履歴と一致しなくなります。
>
> これらの変更をリモートリポジトリにプッシュしようとすると、Git はローカルのコミット履歴がリモートの履歴とコンフリクトしていると判断します。なぜなら、それらは異なるコミットハッシュを持っているからです。
>
> これらの変更を強制的にプッシュするには、`git push -f` を使用する必要があります。これにより、リモートブランチの履歴が上書きされ、ローカルブランチと一致するようになります。

`git push -f` を使用する必要があるため、**プライベートブランチや共有されていないブランチのみ**でリベース操作を行うことが重要です。

### コードのコンフリクトの処理

`git rebase <branch>` を実行した際にコードにコンフリクトが発生した場合、Git は次のようなメッセージを表示します：

```bash
(base) nansenho@mb-nansyou-01 dragonfly_frontend % git rebase develop
Auto-merging tests/e2e/specs/error/404.spec.ts
CONFLICT (add/add): Merge conflict in tests/e2e/specs/error/404.spec.ts
error: could not apply 03652542... fix:404エラーページのe2eテスト
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 03652542... fix:404エラーページのe2eテスト
```

このメッセージは、コンフリクトを解決するための三つの方法を教えてくれます：

- `git rebase --continue`

  `git rebase` の途中でコンフリクトが発生し、手動で全てのコンフリクトを解決した後、`git add <file>` を使って解決済みのファイルをマークします。

  その後、`git rebase --continue` を実行してリベース操作を続行します。

  リベース操作が完了したら、`git push -f` を使用してローカルの変更をリモートブランチにプッシュします。

- `git rebase --abort`

  リベース中に問題が発生し、現在のリベース操作を中止することを決定した場合、`git rebase --abort` を使用できます。

  このコマンドは現在のリベース操作をキャンセルし、リベースを開始する前の状態にブランチを戻します。

- `git rebase --skip`

  現在のコードコンフリクトを無視することを決定した場合、`git rebase --skip` を使用できます。

  このコマンドは現在のコンフリクトを引き起こしているコミットをスキップします。

実際にコンフリクトが発生した場合、**最も推奨される方法**は、手動でコンフリクトを解決した後に `git rebase --continue` を使用してリベースを続行することです。

解決できないコンフリクトが発生した場合や、現在のリベース操作が実行不可能であると判明した場合は、**`git rebase --abort` を使用してリベース操作をキャンセルし、元の状態に戻してから他の解決策を考えることをお勧めします**。

**`git rebase --skip` を使用するのは推奨されません**。

## `git rebase` と `git merge` の使用シーン

一般的に、すべてのブランチの完全なコミット履歴を保持したい場合はマージを使用し、コミット履歴をシンプルかつ直線的に保ちたい場合はリベースを使用します。

具体的には、以下の二つの広く認識されている使用シーンがあります：

- プライベートブランチや共有されていないブランチ（例：`feature-xxx`、`fix-xxx`、`refactor-xxx` など）で開発を行う際、他のブランチの最新コードを取り込む場合、コミット履歴をシンプルかつ直線的に保つために `git rebase` を使用するのが適しています。

- プライベートブランチや共有されていないブランチ（例：`feature-xxx`、`fix-xxx`、`refactor-xxx` など）を共有ブランチ（例：`master`、`develop`、`main` など）にマージする際には、すべてのコミット履歴を記録するために `git merge` を使用し、コード変更の追跡を容易にするのが適しています。

## 参考

- [Git - git-rebase Documentation](https://git-scm.com/docs/git-rebase/en)
- [Git - git-merge Documentation](https://git-scm.com/docs/git-merge/en)