Documentation Index
Fetch the complete documentation index at: https://mintlify.com/git/git/llms.txt
Use this file to discover all available pages before exploring further.
Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project while keeping the commits separate.
What Are Submodules?
A submodule is a repository embedded inside another repository (the superproject). The submodule has its own history, while the superproject tracks which specific commit of the submodule should be checked out.
superproject/
├── .git/
│ └── modules/
│ └── libs/utils/ # Submodule's Git directory
├── .gitmodules # Submodule configuration
├── src/
└── libs/
└── utils/ # Submodule working directory
└── .git # Link to actual Git directory
When to Use Submodules
Good Use Cases
- External dependencies - Pin specific versions of libraries
- Shared code - Reuse code across multiple projects
- Large binary assets - Keep them in a separate, shallow-cloned repository
- Monorepo splitting - Separate access controls for different components
When NOT to Use Submodules
- Simple dependencies (use package managers instead)
- Frequently changing code you control (keep in the main repo)
- When team members aren’t familiar with submodules
Basic Submodule Operations
Adding a Submodule
git submodule add <repository-url> <path>
Example:
git submodule add https://github.com/user/lib.git libs/external-lib
This:
- Clones the repository to
libs/external-lib
- Creates/updates
.gitmodules file
- Stages the changes
The .gitmodules file:
[submodule "libs/external-lib"]
path = libs/external-lib
url = https://github.com/user/lib.git
Cloning a Repository with Submodules
git clone https://github.com/user/project.git
Submodule directories exist but are empty.
Initialize and update submodules
git submodule init
git submodule update
Or do it in one step during clone
git clone --recurse-submodules https://github.com/user/project.git
Updating Submodules
# Update all submodules to latest commit from their tracked branches
git submodule update --remote
# Update specific submodule
git submodule update --remote libs/external-lib
Viewing Submodule Status
# Show submodule status
git submodule status
# Output:
# a1b2c3d libs/external-lib (v1.2.3)
# -f4e5d6c libs/another (heads/main)
Prefix meanings:
- No prefix: Submodule is checked out at the correct commit
-: Submodule is not initialized
+: Submodule is checked out at a different commit than recorded
U: Submodule has merge conflicts
Working with Submodules
Making Changes in a Submodule
Navigate to the submodule
git checkout -b feature-branch
# Edit files
git commit -am "Add new feature"
git push origin feature-branch
cd ../..
git add libs/external-lib
git commit -m "Update external-lib to include new feature"
Updating to a Specific Submodule Version
# Navigate to submodule
cd libs/external-lib
# Check out specific version
git checkout v1.2.3
# Update superproject
cd ../..
git add libs/external-lib
git commit -m "Update external-lib to v1.2.3"
Pulling Changes with Submodules
# Pull changes in superproject
git pull
# Update submodules to match
git submodule update --init --recursive
# Or do both at once
git pull --recurse-submodules
Advanced Submodule Features
Configuring Submodule Behavior
# Set default branch for submodule to track
git config -f .gitmodules submodule.libs/external-lib.branch main
# Set update strategy (merge, rebase, or checkout)
git config -f .gitmodules submodule.libs/external-lib.update merge
# Ignore submodule changes in git status
git config submodule.libs/external-lib.ignore all
Active Submodules
Control which submodules are active:
# Activate only specific submodules
git config submodule.libs/external-lib.active true
# Use pathspec to activate matching submodules
git config submodule.active 'libs/*'
Foreach: Run Commands on All Submodules
# Run command in each submodule
git submodule foreach 'git fetch'
# More complex example
git submodule foreach 'git checkout main && git pull'
# Include superproject
git submodule foreach --recursive 'echo "$name: $path"'
Submodule Workflows
Third-Party Library Workflow
# Add the library
git submodule add https://github.com/third-party/lib.git vendor/lib
git commit -m "Add third-party library"
# Occasionally update to new version
cd vendor/lib
git fetch
git checkout v2.0.0
cd ../..
git add vendor/lib
git commit -m "Update library to v2.0.0"
Shared Code Workflow
# Enable recursive operations by default
git config --global submodule.recurse true
# Clone with submodules
git clone --recurse-submodules <url>
# Regular work - most commands now recurse automatically
git pull
git checkout <branch>
git status
Development Across Repositories
# Work on submodule and superproject simultaneously
cd libs/shared
git checkout -b feature-branch
# Make changes
git commit -am "Add feature to shared library"
git push origin feature-branch
cd ../..
git add libs/shared
git commit -m "Update to shared library feature branch"
Removing Submodules
Deinitialize the submodule
git submodule deinit libs/external-lib
This empties the working directory but keeps the Git directory.
Remove from the working tree and .gitmodules
git commit -m "Remove external-lib submodule"
Optionally, remove the Git directory
rm -rf .git/modules/libs/external-lib
Common Issues and Solutions
Detached HEAD in Submodule
Problem: Submodules are often in detached HEAD state.
Solution: Check out a branch before making changes:
cd libs/external-lib
git checkout main
# Make changes
Submodule Changes Not Showing
Problem: Modified submodules don’t appear in git status.
Solution:
# Show submodule changes
git status --submodule
# Or configure it permanently
git config --global status.submoduleSummary true
Accidentally Committed Submodule Directory
Problem: Added submodule directory as regular files instead of a submodule.
Solution:
# Remove from index but keep files
git rm -r --cached libs/external-lib
# Add as submodule
git submodule add <url> libs/external-lib
Submodule URL Changed
Problem: Submodule repository moved.
Solution:
# Update URL in .gitmodules
git config -f .gitmodules submodule.libs/external-lib.url <new-url>
# Sync to .git/config
git submodule sync
# Update
git submodule update --remote
Alternatives to Submodules
Git Subtree
Pros:
- Simpler for contributors (no special commands)
- History is integrated into the superproject
Cons:
- Harder to push changes back upstream
- Larger repository size
# Add a subtree
git subtree add --prefix libs/external-lib https://github.com/user/lib.git main --squash
Package Managers
For language-specific dependencies:
- npm (JavaScript)
- pip (Python)
- gem (Ruby)
- cargo (Rust)
For large-scale projects:
- Google’s Repo tool
- Meta’s Sapling
- Microsoft’s Rush
Best Practices
-
Pin to specific commits or tags - Don’t track branches in production
-
Document submodule workflow - Add README section explaining submodule usage
-
Use shallow clones for large repositories:
git submodule update --init --depth 1
-
Configure global recurse setting:
git config --global submodule.recurse true
-
Use relative URLs when possible:
[submodule "lib"]
url = ../lib.git # Relative to superproject URL
-
Regular updates - Update submodules regularly to avoid drift
-
Test after updating - Always test the superproject after updating submodules
Configuration Reference
# Global config (~/.gitconfig)
[submodule]
# Recurse into submodules by default
recurse = true
[status]
# Show submodule changes in git status
submoduleSummary = true
[diff]
# Show submodule changes in git diff
submodule = log
# Repository config (.git/config or .gitmodules)
[submodule "libs/external-lib"]
url = https://github.com/user/lib.git
branch = main
update = merge
active = true
ignore = dirty # or: all, untracked, none
Submodule Commands Reference
# Add submodule
git submodule add <url> <path>
# Initialize submodules
git submodule init
# Update submodules
git submodule update
git submodule update --remote
git submodule update --init --recursive
# Status
git submodule status
git submodule summary
# Execute command in all submodules
git submodule foreach '<command>'
# Sync URLs from .gitmodules to .git/config
git submodule sync
# Deinitialize submodule
git submodule deinit <path>
# Remove submodule
git rm <path>