Skip to content

How to Add and Manage Git Submodules

DodaTech 2 min read

In this tutorial, you'll learn about How to Add and Manage Git Submodules. We cover key concepts, practical examples, and best practices.

The Problem

You need to include another Git repository (a shared library, theme, or utility) inside your project while keeping their histories separate. Manually copying files means you lose upstream updates and can't track changes back to the source. Git submodules let you embed one repository inside another as a reference to a specific commit, preserving both histories independently.

Quick Fix

1. Add a submodule

git submodule add https://github.com/example/shared-lib.git libs/shared

This clones the repo into libs/shared and creates a .gitmodules file.

2. Clone a repository with submodules

# Clone and initialize in one step
git clone --recurse-submodules https://github.com/user/project.git

# Or clone first, then initialize
git clone https://github.com/user/project.git
cd project
git submodule update --init --recursive

3. Update submodules to latest commit

# Pull latest for all submodules
git submodule update --remote --merge

# Update a specific submodule
cd libs/shared
git pull origin main
cd ..
git add libs/shared
git commit -m "Update shared-lib to latest"

4. Check submodule status

git submodule status

Expected output:

 abc123def456 libs/shared (heads/main)
 789ghi012jkl libs/theme (v2.1.0)

A - prefix means not initialized, + means modified but not committed to parent.

5. Remove a submodule completely

git submodule deinit -f libs/shared
rm -rf .git/modules/libs/shared
git rm -f libs/shared
git commit -m "Remove shared-lib submodule"

6. Work on a submodule

cd libs/shared
git checkout main
# Make changes, commit
git add .
git commit -m "Fix bug in shared lib"
git push origin main
cd ../..
# Update parent to reference the new commit
git add libs/shared
git commit -m "Update shared-lib with bugfix"

7. Pin submodule to a specific tag

cd libs/shared
git checkout v2.1.0
cd ../..
git add libs/shared
git commit -m "Pin shared-lib to v2.1.0"

Common Causes

Problem Symptom Fix
Submodule not cloned Empty directory git submodule update --init --recursive
Detached HEAD in submodule git status shows detached HEAD git checkout main or a tag
Outdated submodule Submodule points to old commit git submodule update --remote --merge
Changed submodule not tracked Parent repo doesn't show changes git add the submodule path
.gitmodules not committed Other users can't initialize Commit .gitmodules file

Practice with a Test Repository

cd /tmp
mkdir git-practice && cd git-practice
git init --initial-branch=main
# Initialized empty Git repository in /tmp/git-practice/.git/
echo "test" > file.txt && git add . && git commit -m "init"
# [main (root-commit) abc1234] init

Before running destructive commands on your real repository, practice on a throwaway test repository. This builds confidence and prevents costly mistakes. The reflog is your safety net, but practice makes it less needed.

Prevention

  • Pin submodules to a stable tag instead of main for production
  • Document submodule commands in your project's CONTRIBUTING.md
  • Use git submodule status in CI to verify consistent versions across builds

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro