開発者が少ないチームはリポジトリを少なくしよう。Git Subtreeで履歴を残したままリポジトリを統合する

# まとめ
git subtreeの使い方だけ気になる人は、目次からどうぞ

- 少人数の開発チームではリポジトリを少なく保とう
- リポジトリを減らすために、`git subtree`を使ってリポジトリを統合しよう

# はじめに
開発者が5人以下のような、小さなチームではリポジトリが増えてくると管理が難しくなります。

どんなとき不都合が出るんでしょう？

**複数のリポジトリをまたがった変更の差分を確認しづらい**
:stuck_out_tongue:このPull Requestは、別のリポジトリのPull Requestのマージが必要なので、両方とも見ながらレビューして！

**アプリケーションとリポジトリの対応**を覚える必要がある
:worried:このシステムってどのリポジトリだっけ？

**リポジトリ数が増えてくると、リポジトリ権限やリポジトリ設定をする労力が増える**
:angry:このリポジトリ、Slack連携まだしてないじゃん！なんで……？

**複数リポジトリにまたがった課題を管理しづらい**
:anguished:このIssueは複数のリポジトリに、またがっているからリポジトリごとにIssueを作らないといけないね！

これらは、実際にあった例です。このチームは4人に対して、14個のリポジトリを抱えていました :weary: [^2]。リポジトリが増えてくると、 **チームが管理しているリポジトリを一覧するためのドキュメント** も用意されました。

[^2]: 5人のチームならリポジトリの数は、2-3リポジトリに抑えたほうがいいと思います。

そのチームは問題を解決するために、モノリポ[^1]への移行を決意しました。

[^1]: ここでは「**リポジトリの数を少なくして極力1つのリポジトリとしてまとめよう**」という試み

# モノリポの利点
- コードが再利用しやすい
- 開発者同士のコラボレーションが簡単になる
- コミットを通じて依存がシンプルになる
- 根本的なリファクタリングや改修が行える
- 多数のリポジトリを管理するコストから解放される

モノリポにすると、コードがどのリポジトリにあるか明確になります。**リポジトリ一覧のドキュメントなんてもう要りません！**
コードが一箇所に集約されることで、すでにある資産を使い回せますし、アプリケーションをまたいだリファクタリング(**ワークアラウンドではない！**)もできます。

「モノリポいいじゃん」って思いました？

注意が必要です。モノリポには、さまざまなプログラミング言語やビルドシステムを統合する必要があります。ビルドシステムを統合するソリューションとして、

- https://bazel.build
- https://www.pantsbuild.org
- https://buck.build

が提供されています。興味がある人は調べてみてください。

# モノリポへの第一歩
モノリポにすると決断したものの、リポジトリを1つにまとめるにはどうしたらいいでしょう？　とっておきのコマンドがあります。

**[git-subtree](https://manpages.debian.org/testing/git-man/git-subtree.1.en.html)**です。

外部のリポジトリを取り込むことができます。コミットへの参照だけを持つgit-submoduleと違い、完全な履歴として取り込むことができます。直接変更もできますね！

# git-subtreeの使い方
マスターとサブのディレクトリを用意します。マスターは取り込む側、サブは取り込まれる側です。

subtree-masterに、subtree-subのリポジトリを取り込みます。

```shell-session
$ tree
.
├── subtree-master
│   └── README.md
└── subtree-sub
    └── README-sub.md
```

## マスターのGit Log
```shell-session
$ git log --oneline
a3f47ce (HEAD -> master) init
```

## サブのGit Log
```shell-session
$ git log --oneline
d9dbc28 (HEAD -> master) update
ab3553f init(sub)
```

## 実際に取り込む
```shell-session
$ cd subtree-master #マスターのディレクトリに移動
$ git remote add sub-repo ../subtree-sub #リモートリポジトリでもOK
$ git subtree add --prefix=sub sub-repo master #subというディレクトリが切られる
git fetch sub-repo master
warning: no common commits
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From ../subtree-sub
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> sub-repo/master
Added dir 'sub'
```

subというディレクトリ以下に、サブリポジトリの内容が取り込まれます。

```
$ tree
.
├── subtree-master
│   ├── README.md
│   └── sub
│       └── README-sub.md
└── subtree-sub
    └── README-sub.md

3 directories, 3 files
```

マスターのリポジトリでGit Logを確認します。サブリポジトリの中身のコミットが丸々取り込まれていることがわかります。これで履歴を消さずに統合できました。

```shell-session
$ git log --oneline
1d9c231 (HEAD -> master) Add 'sub/' from commit 'd9dbc28107de903e4c1f0c0680a6af9926d031da'
d9dbc28 (sub-repo/master) update
ab3553f init(sub)
a3f47ce init
```

**subディレクトリが不要**だと感じたら、`git mv`で中身を移動したり、ディレクトリを削除してコミットすれば自由にできます。

```
$ tree
.
├── subtree-master
│   ├── README-sub.md
│   └── README.md
└── subtree-sub
    └── README-sub.md

2 directories, 3 files
```

```
$ git log --oneline
a2eadbf (HEAD -> master) ディレクトリを無くしてREADMEをルートへ移動
1d9c231 Add 'sub/' from commit 'd9dbc28107de903e4c1f0c0680a6af9926d031da'
d9dbc28 (sub-repo/master) update
ab3553f init(sub)
a3f47ce init
```

# まとめ
- 少人数の開発チームではリポジトリを少なく保とう
- リポジトリを減らすために、`git subtree`を使ってリポジトリを統合しよう