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 deinitgit rm
  • サブモジュールは特定のコミットを参照する(ブランチではない)
  • git pullだけではサブモジュールは更新されない
  • サブモジュール内の変更は、そのリポジトリにプッシュが必要
  • クローン時は--recurse-submodulesを付けるか、後からgit submodule update --initを実行

サブモジュールは便利な機能ですが、運用が複雑になりがちです。チームで使う場合は、サブモジュールの更新ルールを決めておくことをおすすめします。

Dai Aoki

Dai Aoki