git submoduleメモ

# submoduleを導入する

以下のとおりサブモジュールを追加すると

```bash
$ git submodule add -b master https://github.com/nyandora/sub-module
```

以下の変更がステージングに登録される。

* `.gitmodules`ファイルが追加　or 変更される。
    * 追加：サブモジュールの導入は初の場合
    * 変更：他にサブモジュールがあったり、既存のサブモジュールを全て削除した場合
* `sub-module`という**ファイル**が追加される（ディレクトリではない。以下に`new file`とあることから、ファイルであると分かる）。sub-moduleリポジトリのどのコミットを参照しているか、という情報を持つ。シンボリックリンク的なもの。

```bash
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   .gitmodules
	new file:   sub-module
```

`sub-module`はファイルであり、`sub-module`リポジトリのどのコミットを参照しているか、という情報を持つ。シンボリックリンクのようなもの。ただ、ファイルシステム上ではディレクトリとして扱われる（ややこしい）。

`sub-module`ファイルが参照するコミットIDは、以下コマンドで分かる。

```bash
$ git submodule
 6a6e6214f9a8033d64c6958d3777268f4eb376f2 sub-module (heads/master)
```

親リポジトリは、sub-moduleリポジトリの`6a6e621`のコミットを参照している、ということが分かる。

`.gitmodules`、`sub-module`はまだステージングにあり、これらをcommit, pushして、親リポジトリのリモートリポジトリに反映する。

# 親リポジトリに入ったサブモジュール設定を、他の人が取り込む

この状態で、親リポジトリを新規にcloneする。

その後、以下のとおり`sub-module`の参照先コミットIDを確認する。

```bash
$ git submodule
-6a6e6214f9a8033d64c6958d3777268f4eb376f2 ./
```

先頭に`-`が付いているのは、sub-moduleリポジトリのソースコードをまだローカルに持ってきていない、ということ。

以下のように、sub-moduleリポジトリのソースコードを取得する。

```bash
$ git submodule update --init
Submodule 'sub-module' (https://github.com/nyandora/sub-module.git) registered for path './'
Cloning into '略'...
Submodule path './': checked out '6a6e6214f9a8033d64c6958d3777268f4eb376f2'
```

その後、以下のとおり`sub-module`の参照先コミットIDを確認すると、`-`が無くなっており、ローカルに実際に`6a6e621`のソースコードを持って来れたということが分かる。

```bash
$ git submodule
 6a6e6214f9a8033d64c6958d3777268f4eb376f2 sub-module (heads/master)
```

# サブモジュール側に入った変更を、親リポジトリ側に反映する

まずは、以下のとおり`sub-module`の参照先コミットIDを確認しておく。

```bash
$ git submodule
 6a6e6214f9a8033d64c6958d3777268f4eb376f2 sub-module (heads/master)
```

`sub-module`のリポジトリに何かしら変更を加え、commit, pushする。

その後、親リポジトリ側で以下コマンドで、`sub-module`側の変更を取り込んだかと思いきや・・

```bash
$ git submodule update --recursive
```

実際は以下の通りとなり、参照先のコミットIDが変わらない。ローカルのファイルシステムにも、最新のソースコードが取得されない。

```bash
$ git submodule
 6a6e6214f9a8033d64c6958d3777268f4eb376f2 sub-module (heads/master)
```

`sub-module`側のmasterブランチに追従するには、以下のようにすべき。つまり、`--remote`オプションが必要。`sub-module`のmasterブランチが指す最新のコミットIDの内容が、ローカルに取得される。

```bash
$ git submodule update --remote --recursive
〜省略〜
From https://github.com/nyandora/sub-module
   6a6e621..cecfa10  master     -> origin/master
Submodule path 'sub-module': checked out 'cecfa106b59ee4b2ce8dffbe7c513617b80c70f7'
```

以下のとおり、確かに参照先のコミットIDが変わっている。

```bash
$ git submodule
+cecfa106b59ee4b2ce8dffbe7c513617b80c70f7 sub-module (remotes/origin/HEAD)
```

`+`は、以下が不一致であるという意味。

* ローカルリポジトリ上の最新コミットの`sub-module`ファイルが指すコミットID（多くの場合、古いコミットID）
* 作業ツリーの`sub-module`ファイルが指すコミットID（多くの場合、新しいコミットID。実際にローカルに取得されているソースコードのコミットID）

両者の差分は、以下で分かる。

```bash
$ git diff
diff --git a/sub-module b/sub-module
index 6a6e621..cecfa10 160000
--- a/sub-module
+++ b/sub-module
@@ -1 +1 @@
-Subproject commit 6a6e6214f9a8033d64c6958d3777268f4eb376f2
+Subproject commit cecfa106b59ee4b2ce8dffbe7c513617b80c70f7
```

先ほどのとおり`git submodule update --remote --recursive`を実行すると、以下のように、作業ツリーで`sub-module`が変更されている。

```bash
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   sub-module (new commits)

no changes added to commit (use "git add" and/or "git commit -a")
```

この変更をadd, commit, pushすると、サブモジュール側に入った変更が、親リポジトリ（リモートリポジトリ）に反映される。（最新の`cecfa10`に切り替わっている）

![スクリーンショット 2021-08-02 0.29.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455418/db18f533-26ad-b018-89ea-a9d84980d6d3.png)

このように、親リポジトリ側では、サブモジュール側のどのコミットIDを参照するか、ということを明示的に示す必要がある。

依存先ライブラリが新しくなったら、依存元のリポジトリでバージョンを上げてJARを取得し直すのと同じような感じ。

サブモジュール側に共通処理を入れることが多いと思うが、共通処理を変更したら、それを利用する側のリポジトリ側で今回のような対応を入れる必要がある。要注意。