Git submoduleとは?サブモジュールの追加・更新・削除方法を初心者向けに解説
Git submoduleの基本的な使い方から実践的な管理方法まで初心者向けに解説します。サブモジュールの追加・更新・削除、クローン時の初期化、よくあるトラブルと対処法を学び、外部ライブラリの依存関係を効率的に管理しましょう。
Git submoduleとは
Git submoduleは、あるGitリポジトリの中に別のGitリポジトリを埋め込む機能です。共通のライブラリや外部プロジェクトを、自分のプロジェクト内で独立したリポジトリとして管理できます。
たとえば、複数のプロジェクトで共通のUIコンポーネントを使う場合、そのコンポーネントを独立したリポジトリとして管理し、各プロジェクトからサブモジュールとして参照できます。
サブモジュールの仕組み
サブモジュールは、親リポジトリ内で特定のコミットを参照するポインタとして機能します。
| 要素 | 説明 |
|---|---|
| .gitmodules | サブモジュールの設定ファイル(URL、パス) |
| サブモジュールディレクトリ | 実際のサブモジュールのファイル |
| 参照コミット | 親リポジトリが記録するサブモジュールの特定コミット |
親リポジトリは、サブモジュールの「どのコミットを使うか」を記録します。サブモジュールが更新されても、親リポジトリで明示的に更新しない限り、参照するコミットは変わりません。
サブモジュールの追加
新しいサブモジュールを追加する
# git submodule add <リポジトリURL> <パス>
git submodule add https://github.com/example/library.git libs/library
このコマンドを実行すると、以下のことが起きます。
| 変更 | 内容 |
|---|---|
| .gitmodules作成/更新 | サブモジュールの設定を記録 |
| .git/configに追加 | ローカル設定に追加 |
| ディレクトリ作成 | 指定パスにサブモジュールをクローン |
| ステージング | 変更がステージングされる |
# 追加後、コミットする
git commit -m "サブモジュールlibraryを追加"
.gitmodulesファイルの内容
[submodule "libs/library"]
path = libs/library
url = https://github.com/example/library.git
このファイルはリポジトリにコミットされ、他の開発者がクローンしたときにサブモジュールの情報を取得できます。
特定のブランチを追跡する
デフォルトではサブモジュールは特定のコミットを参照しますが、ブランチを追跡するように設定することも可能です。
# ブランチを追跡するように設定
git submodule add -b main https://github.com/example/library.git libs/library
# または既存のサブモジュールにブランチを設定
git config -f .gitmodules submodule.libs/library.branch main
サブモジュールを含むリポジトリのクローン
クローン時にサブモジュールを初期化
# クローンと同時にサブモジュールを初期化・更新
git clone --recurse-submodules https://github.com/example/project.git
# または --recursive(同じ意味)
git clone --recursive https://github.com/example/project.git
既存のクローンでサブモジュールを初期化
クローン済みのリポジトリでサブモジュールを初期化する場合:
# 1. サブモジュールを初期化
git submodule init
# 2. サブモジュールの内容を取得
git submodule update
# または1と2を一度に実行
git submodule update --init
# ネストしたサブモジュールも含めて初期化
git submodule update --init --recursive
サブモジュールの更新
親リポジトリで参照するコミットを更新
サブモジュールの最新コミットに更新する場合:
# サブモジュールのディレクトリに移動
cd libs/library
# 最新を取得
git fetch
git checkout main
git pull
# 親リポジトリに戻る
cd ../..
# 変更をコミット
git add libs/library
git commit -m "サブモジュールlibraryを更新"
一括で全サブモジュールを更新
# すべてのサブモジュールをリモートの最新に更新
git submodule update --remote
# 特定のサブモジュールのみ更新
git submodule update --remote libs/library
--remoteオプションを使うと、設定されたブランチ(デフォルトはmain/master)の最新コミットに更新されます。
親リポジトリで記録されたコミットに合わせる
他の開発者がサブモジュールを更新した場合、自分の環境を合わせるには:
# 親リポジトリの最新を取得
git pull
# サブモジュールを親リポジトリが記録するコミットに合わせる
git submodule update
git pullだけではサブモジュールの内容は更新されないことに注意してください。
サブモジュールの削除
サブモジュールの削除は少し複雑です。以下の手順で行います。
# 1. サブモジュールの登録解除
git submodule deinit -f libs/library
# 2. .git/modulesからサブモジュールのディレクトリを削除
rm -rf .git/modules/libs/library
# 3. ワーキングツリーからサブモジュールを削除
git rm -f libs/library
# 4. コミット
git commit -m "サブモジュールlibraryを削除"
| ステップ | 説明 |
|---|---|
| deinit | .git/configからサブモジュールの設定を削除 |
| rm .git/modules/... | キャッシュされたサブモジュールデータを削除 |
| git rm | .gitmodulesと実ファイルを削除 |
サブモジュールのステータス確認
# サブモジュールの状態を確認
git submodule status
出力例:
abc1234 libs/library (v1.0.0)
+def5678 libs/utils (heads/main)
-ghi9012 libs/tools
| 記号 | 意味 |
|---|---|
| (なし) | 親リポジトリが記録するコミットと一致 |
+ | 親リポジトリの記録と異なるコミットをチェックアウト中 |
- | サブモジュールが初期化されていない |
U | マージコンフリクトがある |
実践的な活用シーン
共通ライブラリの管理
複数のプロジェクトで共通のコードを使う場合:
# 各プロジェクトで共通ライブラリを追加
git submodule add https://github.com/company/shared-components.git libs/shared-components
ドキュメントの分離管理
ドキュメントを別リポジトリで管理し、必要なプロジェクトにサブモジュールとして追加:
git submodule add https://github.com/company/docs.git docs
外部依存の固定
特定バージョンの外部ライブラリを使いたい場合:
# サブモジュールを追加
git submodule add https://github.com/external/library.git vendor/library
# 特定のタグに固定
cd vendor/library
git checkout v2.0.0
cd ../..
# 親リポジトリでコミット
git add vendor/library
git commit -m "library v2.0.0を固定"
初心者が混乱しやすいポイント
サブモジュールはコミットを参照する
サブモジュールは特定のコミットを参照しており、ブランチではありません。親リポジトリは「このサブモジュールはコミットabc1234を使う」という情報を記録しています。
# サブモジュールのHEADを確認
cd libs/library
git log -1 --oneline
# abc1234 (HEAD) some commit message
# これはデタッチドHEAD状態
git status
# HEAD detached at abc1234
デタッチドHEADの詳細は別記事で解説しています。
git pullだけではサブモジュールは更新されない
親リポジトリでgit pullしても、サブモジュールの内容は自動更新されません。
# 親リポジトリの更新
git pull
# サブモジュールも更新する必要がある
git submodule update
毎回忘れそうなら、エイリアスを設定しておくと便利です:
git config --global alias.pullall 'pull --recurse-submodules'
サブモジュール内での変更に注意
サブモジュール内で変更を加えた場合、その変更はサブモジュールのリポジトリにコミット・プッシュする必要があります。
# サブモジュール内で作業
cd libs/library
# ... 変更を加える ...
git add .
git commit -m "変更内容"
git push origin main
# 親リポジトリに戻って参照を更新
cd ../..
git add libs/library
git commit -m "サブモジュールlibraryの参照を更新"
git push
サブモジュールへのプッシュを忘れると、他の開発者が親リポジトリをクローンしたときにエラーになります。
空のサブモジュールディレクトリ
クローン時に--recurse-submodulesを付け忘れると、サブモジュールのディレクトリは空になります。
# サブモジュールが空の状態
ls libs/library
# (何も表示されない)
# 初期化と更新を実行
git submodule update --init --recursive
トラブルシューティング
サブモジュールの参照コミットが見つからない
fatal: reference is not a tree: abc1234...
このエラーは、サブモジュールで参照しているコミットがリモートに存在しない場合に発生します。
| 原因 | 対処 |
|---|---|
| サブモジュールの変更がプッシュされていない | サブモジュールのリポジトリでプッシュする |
| 参照が古い | 親リポジトリでサブモジュール参照を更新 |
# サブモジュールをリモートの最新に更新
git submodule update --remote libs/library
git add libs/library
git commit -m "サブモジュール参照を修正"
サブモジュールのURLを変更する
# .gitmodulesを編集
git config -f .gitmodules submodule.libs/library.url https://github.com/new/url.git
# 同期
git submodule sync
# 更新
git submodule update --init --recursive
サブモジュールでコンフリクト
親リポジトリのマージでサブモジュールのコンフリクトが発生した場合:
# どちらのバージョンを使うか決める
cd libs/library
git checkout <使いたいコミット>
cd ../..
# 親リポジトリでマージを完了
git add libs/library
git commit
よく使うコマンド一覧
| 操作 | コマンド |
|---|---|
| サブモジュールを追加 | git submodule add <URL> <パス> |
| 初期化と更新 | git submodule update --init --recursive |
| 状態確認 | git submodule status |
| 全サブモジュールを最新に更新 | git submodule update --remote |
| 親の記録に合わせる | git submodule update |
| サブモジュールを削除 | git submodule deinit -f <パス> → git rm -f <パス> |
| 全サブモジュールでコマンド実行 | git submodule foreach <コマンド> |
foreachで一括操作
# 全サブモジュールでgit pullを実行
git submodule foreach git pull origin main
# 全サブモジュールの状態を確認
git submodule foreach git status
まとめ
Git submoduleは、別のGitリポジトリを自分のリポジトリ内に埋め込む機能です。
| 操作 | コマンド |
|---|---|
| 追加 | git submodule add <URL> <パス> |
| クローン時に初期化 | git clone --recurse-submodules <URL> |
| 既存クローンで初期化 | git submodule update --init --recursive |
| 最新に更新 | git submodule update --remote |
| 削除 | git submodule deinit → git rm |
- サブモジュールは特定のコミットを参照する(ブランチではない)
git pullだけではサブモジュールは更新されない- サブモジュール内の変更は、そのリポジトリにプッシュが必要
- クローン時は
--recurse-submodulesを付けるか、後からgit submodule update --initを実行
サブモジュールは便利な機能ですが、運用が複雑になりがちです。チームで使う場合は、サブモジュールの更新ルールを決めておくことをおすすめします。
Dai Aoki