Skip to content

submodule Git Command Guide

The git submodule command manages external Git repositories embedded within a parent Git repository. Submodules allow you to include other projects as dependencies while maintaining their independent development history and version control.

Terminal window
git submodule [--quiet] [--cached]
git submodule [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
[--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
git submodule [--quiet] status [--cached] [--recursive] [--] [<path>...]
git submodule [--quiet] init [--] [<path>...]
git submodule [--quiet] deinit [-f|--force] (--all| [--] <path>...)
git submodule [--quiet] update [--init] [--remote] [-N|--no-fetch]
[-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow]
[--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>]
[--[no-]single-branch] [--filter <filter-spec>] [--] [<path>...]
git submodule [--quiet] set-branch (--default|--branch <branch>) [--] <path>
git submodule [--quiet] set-url [--] <path> <newurl>
git submodule [--quiet] summary [--cached|--files] [--summary-limit <n>]
[commit] [--] [<path>...]
git submodule [--quiet] foreach [--recursive] <command>
git submodule [--quiet] sync [--recursive] [--] [<path>...]
git submodule [--quiet] absorbgitdirs [--] [<path>...]
OptionDescription
--quietSuppress output messages
--cachedUse commit stored in index
--recursiveOperate recursively on submodules
OptionDescription
-b <branch>Branch to track in submodule
-f, --forceAllow adding submodule to untracked path
--name <name>Name for submodule in .gitmodules
--reference <repository>Reference repository for cloning
--depth <depth>Create shallow clone
OptionDescription
--initInitialize submodules if needed
--remoteUpdate to latest remote commit
-N, --no-fetchDon’t fetch, use existing data
--checkoutCheckout commit (default)
--mergeMerge remote changes
--rebaseRebase local changes
--jobs <n>Parallel jobs for cloning
--single-branchClone only tracked branch
ParameterDescription
<repository>URL or path to submodule repository
<path>Local path for submodule
<branch>Branch to track
<name>Submodule name
<newurl>New URL for submodule
<command>Command to run in each submodule
Repository with Submodules:
├── Parent Repository
│ ├── .git/
│ ├── .gitmodules (submodule configuration)
│ ├── lib/dependency/ (submodule directory)
│ │ ├── .git (points to parent .git/modules/)
│ │ └── [submodule files]
│ └── [parent files]
Submodule States:
├── Initialized: .gitmodules entry exists
├── Cloned: Submodule directory exists with files
├── Updated: Working tree matches committed reference
├── Modified: Working tree differs from committed reference
└── Detached HEAD: Not on a branch in submodule
Dependency Management Comparison:
├── Submodules: Full Git history, independent development
├── Vendor Directory: Simple copy, no version control
├── Git Subtree: Merged history, single repository
├── Package Managers: External dependencies, no Git integration
└── Monorepo: All code in single repository
Terminal window
# Add submodule from repository
git submodule add https://github.com/user/library lib/library
# Add submodule on specific branch
git submodule add -b v2.1 https://github.com/user/library lib/library
# Add with custom name
git submodule add --name my-lib https://github.com/user/library lib/library
# Add shallow submodule
git submodule add --depth 1 https://github.com/user/library lib/library
Terminal window
# Initialize all submodules
git submodule init
# Initialize specific submodule
git submodule init lib/library
# Initialize and update in one step
git submodule update --init
Terminal window
# Update all submodules to committed versions
git submodule update
# Update to latest remote commits
git submodule update --remote
# Update with merge strategy
git submodule update --remote --merge
# Update recursively (submodules of submodules)
git submodule update --recursive
Terminal window
# Show submodule status
git submodule status
# Show cached status (what's committed)
git submodule status --cached
# Show recursive status
git submodule status --recursive
Terminal window
# Enter submodule directory
cd lib/library
# Work normally in submodule
git checkout feature-branch
git pull origin feature-branch
# Make changes, commit, push
# Return to parent and update reference
cd ../..
git add lib/library
git commit -m "Update library submodule to latest"
Terminal window
# Change submodule URL
git submodule set-url lib/library https://new-url.com/library.git
# Sync URLs from .gitmodules
git submodule sync
# Sync recursively
git submodule sync --recursive
Terminal window
# Set branch to track in submodule
git submodule set-branch --branch develop lib/library
# Set default branch tracking
git submodule set-branch --default lib/library
# Check current branch tracking
git submodule status --recursive
Terminal window
# Run command in all submodules
git submodule foreach 'git status --short'
# Run command recursively
git submodule foreach --recursive 'git pull origin main'
# Custom foreach with variables
git submodule foreach 'echo $name: $(git rev-parse HEAD)'
Terminal window
# Configure submodule behavior
git config submodule.recurse true # Recurse by default
git config submodule.fetchJobs 4 # Parallel fetching
git config submodule.alternateLocation /path/to/cache # Reference repos
# Configure submodule cloning
git config submodule.clone.quiet true # Quiet cloning
git config submodule.update.quiet true # Quiet updates
# Configure colors
git config color.submodule.add green
git config color.submodule.modify red
git config color.submodule.delete yellow
# .gitmodules file structure
[submodule "lib/library"]
path = lib/library
url = https://github.com/user/library.git
branch = main
ignore = dirty
update = checkout
shallow = true
[submodule "vendor/dependency"]
path = vendor/dependency
url = ../relative/path/to/dependency.git
branch = stable
Terminal window
# Use relative URLs when possible
git submodule add ../relative/library.git lib/library
# Pin to specific commits, not branches
git add lib/library # Records current submodule commit
# Keep submodules shallow when possible
git submodule add --depth 1 <url> <path>
# Use descriptive paths
git submodule add <url> vendor/libraries/json-parser
# Document submodule purposes in README
Terminal window
# Check before committing submodule changes
git submodule status
# Backup before major operations
cp -r lib/library lib/library.backup
# Verify submodule integrity
git submodule foreach 'git fsck --unreachable'
#!/bin/bash
# Convert monorepo components to submodules
extract_component() {
local component="$1"
local new_repo_url="$2"
# Create new repository
git subtree push --prefix="$component" "$new_repo_url" main
# Replace with submodule
rm -rf "$component"
git rm -r "$component"
git commit -m "Remove $component for submodule conversion"
git submodule add "$new_repo_url" "$component"
git commit -m "Add $component as submodule"
}
extract_component "shared/utils" "https://github.com/company/utils.git"
Terminal window
# Automated dependency updates
update_dependencies() {
echo "Updating project dependencies"
# Update all submodules to latest
git submodule update --remote --recursive
# Check for breaking changes
git submodule foreach --recursive '
if git log --oneline HEAD~1..HEAD | grep -i "break"; then
echo "WARNING: Breaking changes in $name"
fi
'
# Run tests with updated dependencies
if ./run-integration-tests.sh; then
git add .
git commit -m "Update dependencies to latest versions"
echo "Dependencies updated successfully"
else
echo "Tests failed - reverting dependency updates"
git submodule update # Revert to previous versions
fi
}
update_dependencies
Terminal window
# CI/CD pipeline with submodules
ci_build_with_submodules() {
echo "Building project with submodules"
# Shallow clone with submodules
git clone --depth 1 --recurse-submodules <repo-url> .
# Or initialize submodules in existing clone
git submodule update --init --recursive --depth 1
# Verify submodule integrity
git submodule foreach --recursive '
if ! git diff --quiet; then
echo "ERROR: $name has uncommitted changes"
exit 1
fi
'
# Build with dependencies
make all
echo "Build completed successfully"
}
ci_build_with_submodules
Terminal window
# Initialize forgotten submodules
git submodule init
git submodule update
# Check .gitmodules exists
cat .gitmodules
# Verify submodule directories exist
ls -la lib/library/
Terminal window
# Fix detached HEAD state
cd lib/library
git checkout main # Or appropriate branch
cd ../..
# Update parent to track branch
git submodule set-branch --branch main lib/library
git submodule update --remote
Terminal window
# Update submodule URL
git submodule set-url lib/library https://new-url.com/library.git
# Sync all URLs
git submodule sync --recursive
# Update .gitmodules manually if needed
git add .gitmodules
git commit -m "Update submodule URLs"
Terminal window
# Handle merge conflicts in submodules
git status # Shows submodule conflicts
# Resolve in submodule
cd lib/library
git checkout <resolved-commit>
cd ../..
# Stage resolution
git add lib/library
Terminal window
# Fetch missing commits
git submodule update --init --recursive
# Force update if needed
git submodule update --force --recursive
# Check submodule remotes
git submodule foreach 'git remote -v'
Terminal window
# Speed up submodule operations
git submodule update --jobs 4 # Parallel updates
# Use shallow clones
git submodule add --depth 1 <url> <path>
# Limit recursion depth
git submodule update --recursive --depth 1
Terminal window
# Fix permission issues
chmod +x lib/library/.git/hooks/*
# Handle case sensitivity
git config core.ignorecase false
# Fix line ending issues
git submodule foreach 'git config core.autocrlf input'
Terminal window
# Recover corrupted submodule
rm -rf lib/library
git submodule update --init lib/library
# Clean submodule cache
rm -rf .git/modules/lib/library
git submodule update --init lib/library
Terminal window
# Handle authentication problems
git config submodule.lib/library.url https://user:token@github.com/user/library.git
# Use SSH instead of HTTPS
git submodule set-url lib/library git@github.com:user/library.git
# Configure credential helper
git config credential.helper store
#!/bin/bash
# Manage library dependencies with submodules
manage_dependencies() {
echo "=== Library Dependency Management ==="
# Add new dependency
add_dependency() {
local name="$1"
local url="$2"
local path="vendor/$name"
if [ -d "$path" ]; then
echo "Dependency $name already exists"
return 1
fi
git submodule add --depth 1 "$url" "$path"
git commit -m "Add $name dependency"
echo "Added dependency: $name"
}
# Update all dependencies
update_dependencies() {
git submodule update --remote --recursive
git add .
git commit -m "Update all dependencies to latest"
}
# Remove dependency
remove_dependency() {
local name="$1"
local path="vendor/$name"
if [ ! -d "$path" ]; then
echo "Dependency $name not found"
return 1
fi
git submodule deinit "$path"
git rm "$path"
rm -rf ".git/modules/$path"
git commit -m "Remove $name dependency"
}
# List dependencies
list_dependencies() {
echo "Current dependencies:"
git submodule status | while read -r line; do
status=$(echo "$line" | cut -c1)
commit=$(echo "$line" | cut -c2-9)
path=$(echo "$line" | cut -d' ' -f2)
name=$(basename "$path")
case "$status" in
"+") state="modified" ;;
"-") state="uninitialized" ;;
"U") state="merge conflict" ;;
*) state="ok" ;;
esac
echo " $name ($commit) - $state"
done
}
# Command dispatcher
case "$1" in
add) add_dependency "$2" "$3" ;;
update) update_dependencies ;;
remove) remove_dependency "$2" ;;
list) list_dependencies ;;
*) echo "Usage: $0 {add <name> <url>|update|remove <name>|list}" ;;
esac
}
manage_dependencies "$@"
Terminal window
# Manage application components
component_architecture() {
echo "=== Component-Based Architecture ==="
# Component structure
components=(
"frontend:ui-components"
"backend:api-server"
"shared:common-utils"
"mobile:react-native-app"
)
# Initialize all components
init_components() {
for component in "${components[@]}"; do
name="${component%%:*}"
repo="${component#*:}"
if [ ! -d "components/$name" ]; then
echo "Initializing component: $name"
git submodule add "https://github.com/company/$repo.git" "components/$name"
fi
done
git submodule update --init --recursive
git commit -m "Initialize all application components"
}
# Update components
update_components() {
git submodule update --remote --recursive
# Check for API breaking changes
git submodule foreach --recursive '
if git log --oneline --grep="BREAKING" HEAD~5..HEAD | grep -q .; then
echo "WARNING: Breaking changes in $name"
fi
'
git add .
git commit -m "Update components to latest versions"
}
# Develop component
develop_component() {
local component="$1"
if [ ! -d "components/$component" ]; then
echo "Component $component not found"
return 1
fi
echo "Entering development mode for: $component"
cd "components/$component"
# Create feature branch
git checkout -b "feature/$(date +%Y%m%d-%H%M%S)"
echo "Make your changes, then run:"
echo "git add . && git commit -m 'Your changes'"
echo "cd ../.. && git add components/$component"
echo "git commit -m 'Update $component component'"
}
# Release components
release_components() {
local version="$1"
echo "Creating release $version for all components"
# Tag each component
git submodule foreach "git tag -a v$version -m 'Release v$version'"
# Update parent references
git add .
git commit -m "Release v$version - update all component references"
# Create parent release tag
git tag -a "v$version" -m "Release v$version - all components"
}
case "$1" in
init) init_components ;;
update) update_components ;;
develop) develop_component "$2" ;;
release) release_components "$2" ;;
*) echo "Usage: $0 {init|update|develop <component>|release <version>}" ;;
esac
}
component_architecture "$@"
Terminal window
# Manage platform-specific code
cross_platform_setup() {
echo "=== Cross-Platform Development Setup ==="
platforms=(
"web:frontend-web"
"ios:frontend-ios"
"android:frontend-android"
"desktop:frontend-desktop"
)
# Setup platform branches
setup_platforms() {
for platform in "${platforms[@]}"; do
name="${platform%%:*}"
repo="${platform#*:}"
echo "Setting up $name platform"
# Add as submodule
git submodule add "https://github.com/company/$repo.git" "platforms/$name"
# Configure branch tracking
git submodule set-branch --branch "platform/$name" "platforms/$name"
done
git submodule update --init --remote
git commit -m "Setup cross-platform development structure"
}
# Sync platform changes
sync_platforms() {
echo "Syncing platform changes"
git submodule foreach --recursive '
echo "Syncing $name..."
git pull origin $(git rev-parse --abbrev-ref HEAD)
'
# Merge platform changes if needed
for platform in "${platforms[@]}"; do
name="${platform%%:*}"
if git diff --quiet "platforms/$name"; then
echo "$name: No changes"
else
echo "$name: Changes detected"
git add "platforms/$name"
fi
done
if git diff --cached --quiet; then
echo "All platforms synchronized"
else
git commit -m "Sync platform changes"
fi
}
# Build all platforms
build_all_platforms() {
echo "Building all platforms"
git submodule foreach '
if [ -f "build.sh" ]; then
echo "Building $name..."
./build.sh
else
echo "No build script for $name"
fi
'
}
case "$1" in
setup) setup_platforms ;;
sync) sync_platforms ;;
build) build_all_platforms ;;
*) echo "Usage: $0 {setup|sync|build}" ;;
esac
}
cross_platform_setup "$@"
Terminal window
# Manage research experiments
research_submodules() {
echo "=== Research Experiment Management ==="
# Experiment tracking
experiments_dir="experiments"
# Create new experiment
create_experiment() {
local name="$1"
local base_repo="${2:-main}"
experiment_path="$experiments_dir/$name"
if [ -d "$experiment_path" ]; then
echo "Experiment $name already exists"
return 1
fi
# Create experiment as submodule
git submodule add --branch "$base_repo" . "$experiment_path"
# Enter experiment
cd "$experiment_path"
git checkout -b "experiment/$name"
cd -
git add .
git commit -m "Create experiment: $name"
echo "Experiment $name created at $experiment_path"
echo "Work on experiment in: cd $experiment_path"
}
# Archive completed experiment
archive_experiment() {
local name="$1"
local results_summary="$2"
experiment_path="$experiments_dir/$name"
if [ ! -d "$experiment_path" ]; then
echo "Experiment $name not found"
return 1
fi
# Create archive with results
archive_name="experiment-${name}-$(date +%Y%m%d)"
tar czf "${archive_name}.tar.gz" \
"$experiment_path" \
-C /tmp <(echo "$results_summary" > results.txt)
# Remove experiment submodule
git submodule deinit "$experiment_path"
git rm "$experiment_path"
rm -rf ".git/modules/$experiment_path"
git commit -m "Archive experiment $name - $results_summary"
echo "Experiment $name archived as ${archive_name}.tar.gz"
}
# List active experiments
list_experiments() {
echo "Active experiments:"
git submodule status "$experiments_dir"/* 2>/dev/null | while read -r line; do
status=$(echo "$line" | cut -c1)
commit=$(echo "$line" | cut -c2-9)
path=$(echo "$line" | cut -d' ' -f2)
name=$(basename "$path")
case "$status" in
"+") state="modified" ;;
"-") state="uninitialized" ;;
*) state="active" ;;
esac
echo " $name ($commit) - $state"
done
}
case "$1" in
create) create_experiment "$2" "$3" ;;
archive) archive_experiment "$2" "$3" ;;
list) list_experiments ;;
*) echo "Usage: $0 {create <name> [base]|archive <name> <summary>|list}" ;;
esac
}
research_submodules "$@"

What’s the difference between git submodule add and git submodule init?

Section titled “What’s the difference between git submodule add and git submodule init?”

git submodule add registers a new submodule and clones it; git submodule init initializes submodules that are already registered in .gitmodules but not yet cloned.

Use git submodule deinit , then git rm , and finally rm -rf .git/modules/ to completely remove a submodule.

Yes, submodules can be nested. Use —recursive flag with submodule commands to operate on nested submodules.

How do I update a submodule to a specific commit?

Section titled “How do I update a submodule to a specific commit?”

cd into the submodule, checkout the desired commit, then from the parent directory run git add and git commit.

What’s the difference between —remote and regular update?

Section titled “What’s the difference between —remote and regular update?”

—remote updates submodules to the latest commit on the tracked branch; regular update uses the commit recorded in the parent repository.

How do I handle submodule conflicts during merge?

Section titled “How do I handle submodule conflicts during merge?”

Resolve conflicts in the submodule directory first, then stage the resolved submodule reference in the parent repository.

Yes, cd into the submodule and work normally with branches, commits, etc. The parent repository tracks the submodule’s current commit.

How do I clone a repository with submodules?

Section titled “How do I clone a repository with submodules?”

Use git clone —recurse-submodules to clone and initialize all submodules automatically.

.gitmodules stores submodule configuration including URLs, paths, and branch tracking information.

Use git submodule set-url , then git submodule sync to update the configuration.

Yes, use —depth option when adding: git submodule add —depth 1

git submodule status shows the status of all submodules, indicating if they’re initialized, modified, or at the correct commit.

What’s the difference between submodule and subtree?

Section titled “What’s the difference between submodule and subtree?”

Submodules maintain separate repositories and histories; subtree merges external code into the main repository history.

cd into the submodule, commit and push your changes, then update the submodule reference in the parent repository.

Can I have different branches for different submodules?

Section titled “Can I have different branches for different submodules?”

Yes, each submodule can track different branches or even different repositories entirely.

Ensure proper permissions on submodule directories and .git directories. Use git submodule foreach to set permissions across all submodules.

What’s the performance impact of submodules?

Section titled “What’s the performance impact of submodules?”

Submodules can slow down operations, especially with many submodules. Use shallow clones and limit recursion depth when possible.

Yes, but initialize them properly in your CI pipeline using git submodule update —init —recursive.

How do I migrate from submodules to a different approach?

Section titled “How do I migrate from submodules to a different approach?”

Carefully plan the migration, extract submodule code, remove submodule references, and integrate the code using your new approach.

  1. Dependency Management: Include external libraries and frameworks as tracked dependencies
  2. Component Architecture: Break large projects into manageable, independently versioned components
  3. Plugin Systems: Allow extensible architectures with optional components
  4. Cross-Platform Development: Manage platform-specific codebases within a unified project
  5. Research Projects: Isolate experimental code while maintaining project structure
  6. Vendor Code Management: Track third-party code with proper attribution and updates
  7. Monorepo Migration: Gradually convert monolithic repositories to component-based structures
  8. Continuous Integration: Enable modular testing and deployment pipelines