【Git】git submoduleの使い方をわかりやすく解説
はじめに
Gitでプロジェクトを管理する際、複数のリポジトリを連携させたいと感じることはないでしょうか。
例えば、共通のライブラリを別リポジトリで開発している場合に、本体のプロジェクトから参照するときに工夫が必要になります。
こうした場面でよく利用されるのが git submodule です。
ただ、実際に使うときに「どうやって設定するのか」「更新手順がわからない」「運用で注意すべき点は何か」など、さまざまな疑問が浮かぶかもしれません。
ここでは、初心者の方に向けて git submodule の基礎から実務的な活用例までをわかりやすく解説します。
この記事を読むとわかること
- git submodule の基本的な仕組み
- よく使われる主要コマンドと実際の動き方
- 実務で役立つ活用例や運用上の注意点
- 複数のリポジトリを効率よく連携させるための考え方
git submoduleの概要
Gitはソースコードをバージョン管理する仕組みとして非常に有名ですが、その中でも submodule は「リポジトリの中に別のリポジトリを組み込む」ための機能です。
イメージとしては、あるリポジトリ(A)が親となり、別のリポジトリ(B)をサブディレクトリとして内包する形になります。
しかも、ただのファイルコピーではなく、Bリポジトリが持っている履歴やブランチ構成を保ちつつ、Aリポジトリの一部として利用できます。
submoduleが必要になるシーン
例えば、大きなプロジェクトで何度も使う共通のユーティリティ関数群を別リポジトリとして管理しているケースを考えてみましょう。
- 共通機能を単独リポジトリとして開発している
- プロジェクト本体から常に最新バージョンを参照したい
- しかし、バージョン管理上は、ある段階での安定バージョンを固定したいケースもある
こうした状況で git submodule を使うことで、本体と共通リポジトリを切り分けつつ、あたかも一体化されたように扱うことができます。
submoduleを使うメリット
共通コードを一元管理 できる
複数プロジェクトから同じリポジトリを参照し、同じコードを使い回すことが容易になります。
履歴を独立して管理 できる
親リポジトリでの変更と、サブモジュールでの変更が分離されるため、それぞれのコミットログが混在しません。
特定バージョンを指定 できる
submoduleでは、特定のコミットハッシュを参照するよう設定できるため、本体側からすると「安定稼働するバージョン」を固定的に利用できます。
一方で、更新の手間が増えたり、意図せずバージョンが古いままになったりという懸念も出てきます。
しかし、運用を整理しておけば、とても便利な仕組みになります。
Git Submoduleでよく使われるコマンド
ここでは、実際に git submodule を扱うときによく使うコマンドを簡単な例とともに見ていきます。
git submodule add
新たにsubmoduleを追加するときに使うコマンドです。
git submodule add <サブモジュールとなるリポジトリURL> <ディレクトリパス>
例えば、utils
ディレクトリに共通ライブラリを追加したい場合は、以下のようになります。
git submodule add https://github.com/example/common-utils.git utils
これを実行すると、.gitmodules
という設定ファイルにサブモジュールの情報が追記されます。
次に必要であれば、git commit -m "Add submodule"
としてコミットを行いましょう。
git submodule init & update
submoduleを初期化し、親リポジトリで参照している特定のコミットを取得するコマンドです。
git submodule init git submodule update
初めてリポジトリをクローンしたタイミングなどで、submoduleの実体ファイルを取得するために使用することが多いです。
なお、一度 init した後なら git submodule update --init --recursive
のように、ネストされたsubmoduleまでまとめて取得するオプションをつけることもできます。
git submodule status
現在のサブモジュールの状態を一覧で確認できます。
git submodule status
コミットハッシュとサブモジュールのパスが表示され、現在のチェックアウト状況を把握するのに役立ちます。
git submodule update --remote
サブモジュール側の最新コミットを取得して更新する場合に使うコマンドです。
git submodule update --remote
ただし、この操作により親リポジトリの submodule が参照するコミットも変わるため、更新後は親リポジトリ側でコミットを作成するのを忘れないようにしましょう。
git submodule deinit
サブモジュールを初期化状態から外す場合や、不要になったサブモジュールの設定をクリアしたい場合に使用します。
git submodule deinit <ディレクトリパス>
サブモジュールを単に削除しただけでは .gitmodules
に情報が残り続けてしまうので、整理のためにこのコマンドを使うことがあります。
実務での活用シーン
ここでは、submoduleが役に立つ具体的なシーンを紹介します。
共通コンポーネントの使い回し
Webアプリの中で、複数のサービスで共通利用している認証機能やUIコンポーネントなどを、別のリポジトリで開発するケースがあります。
このとき、git submodule を使うと、以下のようなメリットが得られます。
- 親リポジトリと共通コンポーネントの履歴管理が分離される
- 複数サービスで同じバージョンのコンポーネントを利用しやすい
- 必要に応じて特定コミットへ固定しておける
マイクロサービス開発でのライブラリ管理
マイクロサービスで複数の小さなリポジトリを連携させている場合、共通で使う設定ファイルやログ出力の仕組みなどを submodule 化しておくことで、一括管理が容易になります。
たとえば、「サービスA」「サービスB」「サービスC」の3つのリポジトリそれぞれから、共通ライブラリを submodule として読み込む形です。
1つの場所でライブラリを更新したら、それを各サービスが取り込むだけで同じバージョンを確保できます。
一方で、各サービスごとにタイミングを見て更新することもできるため、依存関係をコントロールしやすくなります。
大規模プロジェクトの分割
巨大なリポジトリを1つにまとめると、ブランチを切るたびに膨大な履歴やファイルを抱え込んで作業が煩雑になるケースもあります。
そこで、git submodule によって大きなプロジェクトをいくつかの機能単位に分割し、必要に応じて組み合わせる方式をとることがあります。
こうすることで、変更が頻繁に発生する箇所だけを独立リポジトリとして開発し、メインプロジェクト側は安定した状態を維持しやすくなります。
Git Submodule運用時の注意点
バージョン更新時のコミット漏れ
submodule側を更新したあと、親リポジトリでコミットをしないまま放置してしまうと、チームメンバーとバージョンが同期しない問題が起きがちです。
そのため、更新後には必ず親リポジトリでもコミットを作り、変更点を push するようにしましょう。
サブモジュールを更新しても、親リポジトリが参照するコミットが変わっていなければ、他のメンバーは古いバージョンのまま利用することになります。
サブモジュールのパス移動や削除
サブモジュールを後から移動したり削除したりするときは、.gitmodules
や .git/config
内の情報も整理する必要があります。
単純にディレクトリをリネームしても正常に動かなくなるケースがあるので、サブモジュール関連のコマンドを使って操作を行いましょう。
リポジトリのクローンタイミング
初めてプロジェクトをクローンするとき、git clone
だけではsubmoduleの中身が取得されません。
git submodule update --init
を追加で実行するのを忘れると、サブモジュールのフォルダが空の状態になってしまいます。
プロジェクトのREADMEやドキュメントに手順を明記しておくと、メンバー間のトラブルが減ります。
よくある疑問やトラブル例
1. submoduleを複数階層で使う場合
リポジトリの中でさらに submodule を持ち、その submodule の中にも submodule がある、といった入れ子状の構造が生じることがあります。
この場合、--recursive
オプションをつけて update コマンドを実行すると、ネストされた submodule までまとめて取得できます。
git submodule update --init --recursive
2. submoduleの更新を巻き戻したいとき
親リポジトリから見た submodule の参照先を、過去のコミットに戻すことは可能です。
ただし、サブモジュール側で進んだコミット自体を削除したり改変したりはできないため、参照するコミットを親リポジトリで変更し、その状態をコミットすればOKです。
3. submoduleよりもsubtreeが適している?
Gitには submodule の代わりに subtree という機能も存在し、実装上はリポジトリを「完全に一体化」する形になります。
subtree ではサブリポジトリがそのまま親リポジトリに統合されるため、別の管理方法を検討したい場合に選択肢となることがあります。
しかし、submoduleの方が履歴を明確に分離しやすいという特性があるので、用途に応じて使い分けるとよいでしょう。
subtreeでは親リポジトリとサブリポジトリのコミット履歴が一緒に管理される一方で、submoduleは双方の履歴を切り分けておけます。
まとめ
git submodule を使うことで、リポジトリの中に別のリポジトリを組み込み、分離された履歴管理とバージョン固定が実現できます。
- 親リポジトリとサブモジュールの独立した管理
- 共通ライブラリやコンポーネントの使い回し
- 特定のコミットバージョンの固定化
こうしたメリットがある一方で、更新手順やコミット漏れには気を配る必要があります。
運用ルールをしっかり決めておけば、複数のプロジェクトを横断して同じライブラリを扱う場面や、マイクロサービス環境でリポジトリを分割管理する場面で、とても便利に活用できるでしょう。
是非、色々なプロジェクトで git submodule を試しながら、自分なりの運用スタイルを確立してみてください。