Git迷宮からの脱出：Linear historyで実現する美しい開発ワークフロー

私の会社で開発しているプロダクトの git がぐちゃぐちゃで、これを何とかしたい。
前職では **Linear history** を採用していて履歴をクリーンに保っていたので、**Linear history** を推しておく。

## 現在の git の問題点

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/c10e6cf0-1b30-bf87-f8e2-b4b7bafed25f.png)

* **コミットが整理されていない**:
  Merge Request（以下、MR）の中で、追加修正があると、同じコミットメッセージで大量にコミットされている(数十コミットの場合もある...)
* **レビュー時に不要なコミットが混ざる**:
  GitLabを使っているため、Mergeのコミットで取り込まれたファイルは変更の一覧には出ないがコミットには残るので、一見するとMR担当者が修正したファイルなのか、MRで変更があったファイルなのかわからない
* **Gitリポジトリの使用するストレージが肥大化**:
  Gitはコミットごとにスナップショットを持つため、大量のコミットがあるとリポジトリのサイズが増大します
* **複雑なmerge履歴**:
  複数のブランチ間で頻繁にmergeを行っており、mergeコミットが多発しています。これにより、履歴が複雑になり、変更の追跡が難しくなっています
* **ブランチ間の依存関係が複雑**:
  各MRブランチが他のMRブランチと依存関係を持つことで、変更の管理が難しくなります。例えば、MR3がMR1の変更を取り込むことで、それぞれの変更が独立していない状況が生まれています
* **mergeコンフリクトのリスク増大**:
  複数のMRブランチ間で相互にmergeを行うことで、コンフリクトが発生するリスクが高まります

### 実際の git ログのグラフ画像

![graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/6ba210a3-c2ce-6911-416f-23130e3dd418.png)

東京の路線図より複雑で、本来見やすくするためのグラフがグラフの役割を果たせない状態です...
そしてめちゃくちゃ重い...
こうなるとSourceTreeは使い物にならないです。Forkならまだ大丈夫。

## Linear historyとは

Linear historyとは、コミット履歴が直線的に並ぶように管理する方法です。
これには以下のようなメリットがあります。

1. **履歴の可読性向上**:
   コミットが順序通りに並んでいるため、変更の流れが理解しやすくなります。履歴を読む際に混乱しにくく、どのコミットがどの変更を導入したのかが明確になります

2. **変更の追跡が容易**:
   特定の変更がいつ、どのように導入されたかを簡単に追跡できます。例えば、「機能Aがバグ修正Bの前に導入されたか後に導入されたか」を確認するのが容易です

3. **Git bisectの有効活用**:
   Git bisectは特定のバグや回帰を導入したコミットを特定するための強力なツールですが、Non-linear historyでは正確な結果を得るのが難しくなります。Linear historyではGit bisectが効果的に機能し、バグの特定が迅速になります

4. **リバートの容易さ**:
   不要なコミットを簡単にリバートできます。コードが大きく変更されていない限り、特定のリリースから特定の機能やバグ修正を簡単に取り除くことができます

5. **開発者間の協力の向上**:
   Linear historyを維持することで、他の開発者が履歴を追跡しやすくなり、チーム全体の協力がスムーズになります。履歴が理解しやすいと、コードレビューや変更の共有が効率的になります

6. **履歴の一貫性**:
   Non-linear historyでは、複数のブランチやmergeが入り乱れることで、履歴が一貫しない場合があります。Linear historyでは、コミットが一貫した順序で並ぶため、履歴全体が論理的で一貫性があります

7. **デバッグの効率化**:
   デバッグ時に特定の問題が発生したコミットを特定しやすくなります。複雑なmerge履歴がないため、問題の原因を追跡する際の混乱が少なくなります

大規模なプロジェクトや複数の開発者が関わるプロジェクトにおいて、コードの履歴を分かりやすく保つための方法として採用されているブランチ戦略です。
GitHubではLinear historyを強制する設定もできます。

![スクリーンショット 2024-06-24 13.04.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/2dd96f21-fd71-06df-44e1-f36cf223b0da.png)



### Non-linear history

これはNon-linear historyです。
複雑で混沌とした開発の流れが一目瞭然です。複数のブランチが絡み合っておりわかりにくいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/1bf6309c-77ce-3289-6e68-1cade2fcd5f7.png)

### Linear history

こちらはLinear historyです。
コミット履歴が直線的に並んでいて非常にシンプルで見やすいことがわかります。
それぞれの変更が順序通りに並んでいるため、どのコミットがどの変更を導入したのかが一目でわかります。
これにより、履歴の可読性が大幅に向上し、変更の追跡が容易になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/94b4e1e7-ba8c-e1cd-3d07-ccf83aa18e3e.png)

## Linear historyのルール

* main への merge 前に最新の main で rebase を行う
* 一度 main に merge したブランチには手を加えない
* mainからMRにmerge、MRからMRにmergeするということは行わない
  mainにmergeされる前は、対象のブランチから分岐させ、mainにmergeされた後はrebaseを行う

![ng.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/73b37fca-574a-68b9-a90c-7bda273ca713.png)

## Linear historyに書き換えよう

では、このような履歴になる前にどうすべきだったのか見ていきましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/1bf6309c-77ce-3289-6e68-1cade2fcd5f7.png)

この履歴ではMR1をmainにmergeする前にrebaseを行うべきでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/0876828a-64d9-80c3-92bb-cd6ca26ff8c0.png)

この状態で、MR1をチェックアウトし、`git rebase a3fddc3` を行います

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/aad04519-bbb9-bb1f-c5b4-c1a8ba5c127c.png)

そうすると、MR1の起点が`a3fddc3`に変わり、最新の main から分岐したことになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/f2957641-8cb6-c3af-886b-50e16a46d270.png)

この状態で main に merge を行うことでLinear historyの履歴にできます。
また、`commit 1-1`〜`commit 1-3` を rebase でまとめて整理し、mergeしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/34d690b6-6e4c-0ada-781e-2bd70db577cc.png)

かなり整理されて見やすくなりました!
リポジトリを重たくしないためにも、コミットもできるだけまとめましょう。
(全く関係ない修正は同じMRでやるべきではないので、基本まとめられるはず)
コミットをまとめることで cherry-pick での運用も容易になります。

## まとめ

現在のgitリポジトリはコミットが整理されておらず、ストレージの肥大化や複雑なmerge履歴、ブランチ間の依存関係が複雑で、mergeコンフリクトのリスクが高まっています。
この状況を改善するために、Linear historyの採用を提案します。

Linear historyを採用することで、次のようなメリットがあります：

 * **履歴の可読性の向上**: 変更の流れが一目でわかりやすくなります。
 * **変更の追跡が容易**: 特定の変更がいつ、どのように導入されたかを簡単に追跡できます。
 * **デバッグの効率化**: 問題の原因を迅速に特定できます。
 * **リバートが容易**: 不要なコミットを簡単にリバートできます。
 * **開発者間の協力向上**: チーム全体での協力がスムーズになります。

これらの理由から、Linear historyを採用することを強く推奨します。