git submoduleは、Gitリポジトリの中に別のGitリポジトリを含めるための仕組みです。外部ライブラリを特定のバージョンで管理したい場合に使いますが、運用が複雑になるため、多くの場合はパッケージマネージャーの利用を推奨します。
submoduleの基本操作
submoduleを追加する
bash
# submoduleを追加
git submodule add https://github.com/user/repo.git path/to/submodule
# 例:libsディレクトリにライブラリを追加
git submodule add https://github.com/example/library.git libs/library
# コミット
git commit -m "Add library as submodule"
submoduleを含むリポジトリをクローンする
bash
# 方法1:クローン後にsubmoduleを初期化
git clone https://github.com/user/main-repo.git
cd main-repo
git submodule init
git submodule update
# 方法2:クローン時に同時にsubmoduleも取得(推奨)
git clone --recurse-submodules https://github.com/user/main-repo.git
submoduleを更新する
bash
# submoduleを最新版に更新
cd path/to/submodule
git pull origin main
# 親リポジトリに戻って変更をコミット
cd ../..
git add path/to/submodule
git commit -m "Update submodule to latest version"
# または、すべてのsubmoduleを一度に更新
git submodule update --remote
submoduleを削除する
bash
# submoduleの登録を解除
git submodule deinit path/to/submodule
# submoduleのディレクトリを削除
git rm path/to/submodule
# コミット
git commit -m "Remove submodule"
実践的な使い方
特定のブランチを追跡する
bash
# submoduleの設定を変更
git config -f .gitmodules submodule.path/to/submodule.branch develop
# submoduleを指定ブランチの最新に更新
git submodule update --remote
submoduleの状態を確認する
bash
# submoduleの一覧と状態を表示
git submodule status
# submoduleの詳細情報を表示
git submodule summary
チーム全員がsubmoduleを更新する
bash
# pullと同時にsubmoduleも更新
git pull --recurse-submodules
# または、pull後に手動で更新
git pull
git submodule update --init --recursive
使ったほうがいい場合
1. 外部ライブラリの特定バージョンを固定したい
bash
# 例:プロジェクトで使うライブラリのバージョンを厳密に管理
git submodule add https://github.com/company/internal-library.git libs/internal
- パッケージマネージャーがない言語やフレームワーク
- 内製ライブラリで厳密なバージョン管理が必要
- ライブラリのソースコードをプロジェクトに含めたい
2. 複数のリポジトリを一元管理したい
bash
# モノレポ的な構成
main-project/
├── backend/ # submodule
├── frontend/ # submodule
└── shared/ # submodule
- マイクロサービスアーキテクチャで複数のサービスを管理
- 各サービスは独立してバージョン管理が必要
- 開発時は全体を一度にcloneしたい
3. ドキュメントや設定ファイルを共有したい
bash
# 複数のプロジェクトで共通の設定を使う
git submodule add https://github.com/company/shared-configs.git config
- 複数プロジェクトで同じドキュメントを使用
- 組織共通の設定ファイルを管理
- 1箇所の更新を複数プロジェクトに反映
使わないほうがいい場合
1. パッケージマネージャーが使える場合
bash
# ❌ 避ける:submoduleでライブラリ管理
git submodule add https://github.com/lodash/lodash.git libs/lodash
# ✅ 推奨:パッケージマネージャーを使う
npm install lodash
理由:
- パッケージマネージャーの方が依存関係の解決が簡単
- バージョン管理が明確(package.jsonなど)
- アップデートやロールバックが容易
2. 頻繁に更新が必要な依存関係
bash
# submoduleは更新が煩雑
cd path/to/submodule
git pull
cd ../..
git add path/to/submodule
git commit -m "Update"
理由:
- 更新のたびに親リポジトリもコミットが必要
- チームメンバー全員が
git submodule updateを実行する必要がある - 忘れると古いバージョンのままになる
3. 初心者が多いプロジェクト
理由:
- submoduleの概念が理解しづらい
git cloneだけではsubmoduleが取得されない- トラブルシューティングが難しい
4. CI/CDで複雑な設定が必要になる
bash
# CIでsubmoduleを扱う場合の設定が必要
git clone --recurse-submodules https://github.com/user/repo.git
理由:
- CI/CDの設定が複雑になる
- ビルド時間が長くなる可能性
- 認証の設定が煩雑
submoduleの問題点
1. 操作が煩雑
通常のgit cloneやgit pullでは不十分で、常に--recurse-submodulesオプションを付ける必要があります。
bash
# 忘れがち
git clone https://github.com/user/repo.git
# → submoduleが空のまま
# 正しい
git clone --recurse-submodules https://github.com/user/repo.git
2. 状態の不整合が起きやすい
bash
# 開発者Aがsubmoduleを更新
cd submodule
git pull
cd ..
git add submodule
git commit -m "Update submodule"
git push
# 開発者Bがpullしただけの場合
git pull # submoduleは古いまま
# → ビルドエラーやバグの原因に
3. ブランチ切り替えが複雑
bash
# ブランチを切り替えた後、submoduleも切り替える必要がある
git checkout feature-branch
git submodule update --init --recursive
4. detached HEAD状態になりやすい
submoduleは特定のコミットを指すため、デフォルトでdetached HEAD状態になります。
bash
# submodule内で作業する場合は注意
cd path/to/submodule
git status # "HEAD detached at abc1234"
# ブランチをチェックアウトする必要がある
git checkout main
代替手段
1. パッケージマネージャーを使う
bash
# Node.js
npm install package-name
# Python
pip install package-name
# Go
go get github.com/user/package
2. monorepo構成を使う
bash
# Turborepo, Nx, Lernaなど
project/
├── packages/
│ ├── app-a/
│ ├── app-b/
│ └── shared/
└── package.json
3. Git worktreeを使う
bash
# 同じリポジトリの別ブランチを別ディレクトリで作業
git worktree add ../feature-branch feature-branch
4. ベンダリング(依存関係をコピー)
bash
# Go言語の例
go mod vendor
# Node.jsの例(非推奨だが可能)
cp -r node_modules/package ./vendor/
submoduleを使う際のベストプラクティス
1. ドキュメントに記載する
README.md markdown
## クローン方法
このリポジトリはsubmoduleを含むため、以下のコマンドでクローンしてください:
git clone --recurse-submodules https://github.com/user/repo.git
2. Makefileやスクリプトで自動化
setup:
git submodule update --init --recursive
update:
git submodule update --remote
git add .
git commit -m "Update submodules"
3. CIの設定を明示
.github/workflows/ci.yml yaml
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
4. チーム内で運用ルールを決める
- submoduleの更新タイミング
- 更新時の通知方法
- トラブル時の対処手順
よくあるトラブルと解決方法
submoduleが空のディレクトリになっている
bash
# 解決方法
git submodule update --init --recursive
submoduleの変更が反映されない
bash
# submodule内の変更を確認
cd path/to/submodule
git status
# 親リポジトリで変更をコミット
cd ../..
git add path/to/submodule
git commit -m "Update submodule reference"
submoduleを削除したのに残っている
bash
# 完全に削除する手順
git submodule deinit -f path/to/submodule
git rm -f path/to/submodule
rm -rf .git/modules/path/to/submodule
git commit -m "Remove submodule completely"