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.
git submodule Syntax:
Section titled “git submodule Syntax:”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>...]Submodule Management Options:
Section titled “Submodule Management Options:”| Option | Description |
|---|---|
--quiet | Suppress output messages |
--cached | Use commit stored in index |
--recursive | Operate recursively on submodules |
Add Submodule Options:
Section titled “Add Submodule Options:”| Option | Description |
|---|---|
-b <branch> | Branch to track in submodule |
-f, --force | Allow adding submodule to untracked path |
--name <name> | Name for submodule in .gitmodules |
--reference <repository> | Reference repository for cloning |
--depth <depth> | Create shallow clone |
Update Options:
Section titled “Update Options:”| Option | Description |
|---|---|
--init | Initialize submodules if needed |
--remote | Update to latest remote commit |
-N, --no-fetch | Don’t fetch, use existing data |
--checkout | Checkout commit (default) |
--merge | Merge remote changes |
--rebase | Rebase local changes |
--jobs <n> | Parallel jobs for cloning |
--single-branch | Clone only tracked branch |
Parameters:
Section titled “Parameters:”| Parameter | Description |
|---|---|
<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 |
Understanding Submodule Concepts:
Section titled “Understanding Submodule Concepts:”Submodule Structure:
Section titled “Submodule Structure:”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 submoduleSubmodule vs Other Approaches:
Section titled “Submodule vs Other Approaches:”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 repositoryBasic Submodule Operations:
Section titled “Basic Submodule Operations:”Adding Submodules:
Section titled “Adding Submodules:”# Add submodule from repositorygit submodule add https://github.com/user/library lib/library
# Add submodule on specific branchgit submodule add -b v2.1 https://github.com/user/library lib/library
# Add with custom namegit submodule add --name my-lib https://github.com/user/library lib/library
# Add shallow submodulegit submodule add --depth 1 https://github.com/user/library lib/libraryInitializing Submodules:
Section titled “Initializing Submodules:”# Initialize all submodulesgit submodule init
# Initialize specific submodulegit submodule init lib/library
# Initialize and update in one stepgit submodule update --initUpdating Submodules:
Section titled “Updating Submodules:”# Update all submodules to committed versionsgit submodule update
# Update to latest remote commitsgit submodule update --remote
# Update with merge strategygit submodule update --remote --merge
# Update recursively (submodules of submodules)git submodule update --recursiveChecking Status:
Section titled “Checking Status:”# Show submodule statusgit submodule status
# Show cached status (what's committed)git submodule status --cached
# Show recursive statusgit submodule status --recursiveAdvanced Submodule Management:
Section titled “Advanced Submodule Management:”Working with Submodules:
Section titled “Working with Submodules:”# Enter submodule directorycd lib/library
# Work normally in submodulegit checkout feature-branchgit pull origin feature-branch# Make changes, commit, push
# Return to parent and update referencecd ../..git add lib/librarygit commit -m "Update library submodule to latest"Changing Submodule URLs:
Section titled “Changing Submodule URLs:”# Change submodule URLgit submodule set-url lib/library https://new-url.com/library.git
# Sync URLs from .gitmodulesgit submodule sync
# Sync recursivelygit submodule sync --recursiveBranch Management:
Section titled “Branch Management:”# Set branch to track in submodulegit submodule set-branch --branch develop lib/library
# Set default branch trackinggit submodule set-branch --default lib/library
# Check current branch trackinggit submodule status --recursiveBulk Operations:
Section titled “Bulk Operations:”# Run command in all submodulesgit submodule foreach 'git status --short'
# Run command recursivelygit submodule foreach --recursive 'git pull origin main'
# Custom foreach with variablesgit submodule foreach 'echo $name: $(git rev-parse HEAD)'Configuration and Best Practices:
Section titled “Configuration and Best Practices:”Git Configuration for Submodules:
Section titled “Git Configuration for Submodules:”# Configure submodule behaviorgit config submodule.recurse true # Recurse by defaultgit config submodule.fetchJobs 4 # Parallel fetchinggit config submodule.alternateLocation /path/to/cache # Reference repos
# Configure submodule cloninggit config submodule.clone.quiet true # Quiet cloninggit config submodule.update.quiet true # Quiet updates
# Configure colorsgit config color.submodule.add greengit config color.submodule.modify redgit config color.submodule.delete yellow.gitmodules Configuration:
Section titled “.gitmodules Configuration:”# .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 = stableSubmodule Best Practices:
Section titled “Submodule Best Practices:”# Use relative URLs when possiblegit submodule add ../relative/library.git lib/library
# Pin to specific commits, not branchesgit add lib/library # Records current submodule commit
# Keep submodules shallow when possiblegit submodule add --depth 1 <url> <path>
# Use descriptive pathsgit submodule add <url> vendor/libraries/json-parser
# Document submodule purposes in READMESafe Submodule Operations:
Section titled “Safe Submodule Operations:”# Check before committing submodule changesgit submodule status
# Backup before major operationscp -r lib/library lib/library.backup
# Verify submodule integritygit submodule foreach 'git fsck --unreachable'Integration with Development Workflows:
Section titled “Integration with Development Workflows:”Monorepo Migration:
Section titled “Monorepo Migration:”#!/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"Dependency Management:
Section titled “Dependency Management:”# Automated dependency updatesupdate_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_dependenciesCI/CD Integration:
Section titled “CI/CD Integration:”# CI/CD pipeline with submodulesci_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_submodulesTroubleshooting Common Issues:
Section titled “Troubleshooting Common Issues:”Submodule Not Initialized:
Section titled “Submodule Not Initialized:”# Initialize forgotten submodulesgit submodule initgit submodule update
# Check .gitmodules existscat .gitmodules
# Verify submodule directories existls -la lib/library/Detached HEAD in Submodules:
Section titled “Detached HEAD in Submodules:”# Fix detached HEAD statecd lib/librarygit checkout main # Or appropriate branchcd ../..
# Update parent to track branchgit submodule set-branch --branch main lib/librarygit submodule update --remoteSubmodule URL Changes:
Section titled “Submodule URL Changes:”# Update submodule URLgit submodule set-url lib/library https://new-url.com/library.git
# Sync all URLsgit submodule sync --recursive
# Update .gitmodules manually if neededgit add .gitmodulesgit commit -m "Update submodule URLs"Submodule Conflicts:
Section titled “Submodule Conflicts:”# Handle merge conflicts in submodulesgit status # Shows submodule conflicts
# Resolve in submodulecd lib/librarygit checkout <resolved-commit>cd ../..
# Stage resolutiongit add lib/libraryMissing Submodule Commits:
Section titled “Missing Submodule Commits:”# Fetch missing commitsgit submodule update --init --recursive
# Force update if neededgit submodule update --force --recursive
# Check submodule remotesgit submodule foreach 'git remote -v'Performance Issues:
Section titled “Performance Issues:”# Speed up submodule operationsgit submodule update --jobs 4 # Parallel updates
# Use shallow clonesgit submodule add --depth 1 <url> <path>
# Limit recursion depthgit submodule update --recursive --depth 1Path and Permission Issues:
Section titled “Path and Permission Issues:”# Fix permission issueschmod +x lib/library/.git/hooks/*
# Handle case sensitivitygit config core.ignorecase false
# Fix line ending issuesgit submodule foreach 'git config core.autocrlf input'Repository Corruption:
Section titled “Repository Corruption:”# Recover corrupted submodulerm -rf lib/librarygit submodule update --init lib/library
# Clean submodule cacherm -rf .git/modules/lib/librarygit submodule update --init lib/libraryRemote Access Issues:
Section titled “Remote Access Issues:”# Handle authentication problemsgit config submodule.lib/library.url https://user:token@github.com/user/library.git
# Use SSH instead of HTTPSgit submodule set-url lib/library git@github.com:user/library.git
# Configure credential helpergit config credential.helper storeReal-World Usage Examples:
Section titled “Real-World Usage Examples:”Library Dependency Management:
Section titled “Library Dependency Management:”#!/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 "$@"Component-Based Architecture:
Section titled “Component-Based Architecture:”# Manage application componentscomponent_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 "$@"Cross-Platform Development:
Section titled “Cross-Platform Development:”# Manage platform-specific codecross_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 "$@"Research and Experimentation:
Section titled “Research and Experimentation:”# Manage research experimentsresearch_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.
How do I remove a submodule completely?
Section titled “How do I remove a submodule completely?”Use git submodule deinit
Can submodules have their own submodules?
Section titled “Can submodules have their own submodules?”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
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.
Can I work on a submodule branch?
Section titled “Can I work on a submodule branch?”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
What’s .gitmodules file for?
Section titled “What’s .gitmodules file for?”.gitmodules stores submodule configuration including URLs, paths, and branch tracking information.
How do I change a submodule’s URL?
Section titled “How do I change a submodule’s URL?”Use git submodule set-url
Can submodules be shallow cloned?
Section titled “Can submodules be shallow cloned?”Yes, use —depth option when adding: git submodule add —depth 1
How do I see submodule status?
Section titled “How do I see submodule status?”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.
How do I push changes in a submodule?
Section titled “How do I push changes in a submodule?”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.
How do I handle submodule permissions?
Section titled “How do I handle submodule permissions?”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.
Can I use submodules with CI/CD?
Section titled “Can I use submodules with CI/CD?”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.
Applications of the git submodule command
Section titled “Applications of the git submodule command”- Dependency Management: Include external libraries and frameworks as tracked dependencies
- Component Architecture: Break large projects into manageable, independently versioned components
- Plugin Systems: Allow extensible architectures with optional components
- Cross-Platform Development: Manage platform-specific codebases within a unified project
- Research Projects: Isolate experimental code while maintaining project structure
- Vendor Code Management: Track third-party code with proper attribution and updates
- Monorepo Migration: Gradually convert monolithic repositories to component-based structures
- Continuous Integration: Enable modular testing and deployment pipelines