🌻

とりあえず使えるようになりたいGit Submodule

2022/09/07に公開

とりあえず使えるようになりたいGit Submodule

新しいチームのプロジェクトに入ったらGit Submodule使っていた。
Gitは使ったことあるが、Submoduleは使ったことがない。詳しいことや、構築はいいからとりあえず使えるようになりたい。
そんな人のためのドキュメントです。

Git Submoduleとは

Git Submoduleを使うと

「ふたつのプロジェクトはそれぞれ別のものとして管理したい。だけど、一方を他方の一部としても使いたい」という問題

を解決できる。

サブモジュールを使うと、ある Git リポジトリを別の Git リポジトリのサブディレクトリとして扱うことができるようになります。

引用元:https://git-scm.com/book/ja/v2/Git-のさまざまなツール-サブモジュール

Submoduleを含むプロジェクト

さっそくSubmoduleを含むプロジェクトを見てみましょう。
Submoduleを含むプロジェクトhttps://github.com/nagaoka-aya/GitSubmoduleHundson_projectP.gitをクローンしてください。

クローン方法から早速通常のプロジェクトとは異なります。下記を参照してクローンしてください。
Git Submoduleを含むプロジェクトのクローン【Git Command編】
Git Submoduleを含むプロジェクトのクローン【SourceTree編】

クローンしたプロジェクトは以下のようなディレクトリ構成になっています。

GitSubmoduleHundson_projectP
    │  .gitmodules
    │  README.md
    │
    └─submodule
        └─GitSubmoduleHundson_projectS
                README.md

GitSubmoduleHundson_projectPではGitSubmoduleHundson_projectSがSubmoduleとして登録されています。
ではGitHubブラウザ上ではどのように見えるか確認してみましょう。下記にアクセスしてください。
https://github.com/nagaoka-aya/GitSubmoduleHundson_projectP/tree/main/submodule

GitHubブラウザ上ではGitSubmoduleHundson_projectSというディレクトリではなく、GitSubmoduleHundson_projectSリポジトリへのリンクになっています。
そしてリンクを押すとGitSubmoduleHundson_projectSリポジトリに遷移します。まさにGit リポジトリを別の Git リポジトリのサブディレクトリとして扱うということがわかったかと思います。

Submoduleはどのリポジトリの、どの時点を管理する

GitHubブラウザ上ではSubmoduleがGitSubmoduleHundson_projectS @ b5a55c9と表示されています。このb5a55c9はコミット番号です。
つまりGitSubmoduleHundson_projectSのコミット番号b5a55c9を参照する、という意味になります。
ここからわかる通り、Git Submoduleはサブモジュールとして登録したリポジトリの最新を取得する、というわけではありません。
親プロジェクト(この例ではGitSubmoduleHundson_projectP)は常にSubmoduleのどのコミットを参照するかを保持しているのです。そしてユーザーは常にSubmoduleのどのコミットを参照させるかをコントロールする必要があります。

では、GitHubブラウザではなく、クローンしたプロジェクトのサブモジュールがどのコミットを参照しているのか確認してみましょう。
先ほどクローンしたGitSubmoduleHundson_projectPで以下を実行してみてください。
Submoduleのコミット番号を確認する【Git Command編】
Submoduleのコミット番号を確認する【SourceTree編】

他人が更新したSubmoduleを取り込む

せっかちな人のために重要なことを先に書きます。他人が更新したSubmoduleを取り込むには2つの手順が必要です。

  1. 親プロジェクトを更新(pull、checkoutなど)する
  2. サブモジュールを更新(submodule update)する

どういうことかを詳しく書きます。

GitSubmoduleHundson_projectPとGitSubmoduleHundson_projectSにブランチfix/hogehogeを作成しそれぞれREADME.mdファイルに「ホゲホゲ」と追記したとしましょう。
GitHubブラウザ上ではまさにそうなっていることが確認できます。
https://github.com/nagaoka-aya/GitSubmoduleHundson_projectP/blob/fix/hogehoge/README.md
https://github.com/nagaoka-aya/GitSubmoduleHundson_projectP/tree/fix/hogehoge/submodule (Submoduleのリンクを押してください)
リモート上のリポジトリではSubmoduleの参照先が更新後(README.mdファイルに「ホゲホゲ」と追記した後)の状態であることがわかったと思います。

では早速ローカルにその変更を取り込んでみましょう。GitSubmoduleHundson_projectPプロジェクトでfix/hogehogeブランチをcheckoutしてください。
チェックアウトしたらローカルのGitSubmoduleHundson_projectP\README.mdGitSubmoduleHundson_projectP\submodule\GitSubmoduleHundson_projectS\README.mdを見てください。
前者は「ホゲホゲ」と追記されていますが、後者は「ホゲホゲ」と追記されていないことがわかると思います。
(ただしSourcetreeを使っている場合Checkout時はsubmodule updateも一緒にやってくれるようなので、この通りにはなりません。一方Pullの時はSubmoduleの取得がされないため以降の手順が必要になります。)
つまりGitSubmoduleHundson_projectSに対する更新が取得できていません。

ダメ押しでローカルでSubmoduleが今どのコミット番号を参照しているのかを見てみましょう。以下を実行してみてください。
Submoduleのコミット番号を確認する【Git Command編】
Submoduleのコミット番号を確認する【SourceTree編】
1ebdb6c~から始まるコミット番号を参照していてほしいのに、全く違うコミット番号(checkout前のコミット番号)が取得されたと思います。

そうです、Gitではcheckoutやpullを実行しただけではSubmoduleの参照先は変更されません。
明示的にSubmoduleを更新する必要があります。そのため最初に書いたように【2. サブモジュールを更新(submodule update)する】が必要になります。

では実際にサブモジュールを更新してみましょう。以下を実行してみてください。
Submoduleを更新する【Git Command編】
Submoduleを更新する【SourceTree編】

再びGitSubmoduleHundson_projectP\submodule\GitSubmoduleHundson_projectS\README.mdを確認してください。今度は「ホゲホゲ」と追記されていると思います。
これで他人が更新したサブモジュールを取得することができました。

自分がSubmoduleを更新する

せっかちな人のために重要なことを先に書きます。Submoduleを更新したら二つの作業が必要です。

  1. Submoduleの変更をSubmoduleのリポジトリにPushする
  2. Submoduleの参照先の変更を親リポジトリにPushする

どういうことかを詳しく書きます。

あなたはGitSubmoduleHundson_projectS\README.mdをBと更新し、GitSubmoduleHundson_projectSにプッシュします。
Submoduleの内容を変更したときは、通常のGitリポジトリと同じでGitSubmoduleHundson_projectSディレクトリに移動しcommit&Pushします。
プッシュはできませんが是非commitまで実際にGitSubmoduleHundson_projectSに対して行ってみてください。

ではこの状態でYさんがGitSubmoduleHundson_projectPをpullし、忘れずにsubmodule updateもしたらどうなるでしょうか。
あなたはYさんのローカルにもBと更新されたREADME.mdが届くことを期待すると思いますが、実際はそうではありません。
最初に述べたように親プロジェクトはどのリポジトリの、どの時点を参照するか管理しています。現時点ではprojectSのコミット番号1を参照すると管理されているのでsubmodule updateをしてもコミット番号1の状態が取得されてしまうわけです。

そこで最初に記載した【2. 親プロジェクトのSubmoduleの参照先を親プロジェクトにPushする】を実施します。

実際にはSubmoduleを更新すると親プロジェクトではSubmoduleと同名のファイルとして差分が表示されます。
実際にGitSubmoduleHundson_projectPプロジェクトの差分を確認してみてください。GitSubmoduleHundson_projectP自体には変更を加えていないにも関わらず差分が表示されていると思います。
それはSubmoduleの参照先が変わっていることを表します。この差分をCommitしPushすれば良いのです。

これでYさんはsubmodule updateであなたの変更を取り込むことができます。

Discussion