iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Submodules

Git submodules: nested repositories with pinned commits. When to use, when to avoid, and the alternatives.

Git — submodules

EXAMPLE
# ===== What submodules are =====
# A submodule is a reference to a SPECIFIC COMMIT of another repository,
# placed at a path inside your repo. Your repo tracks: the URL + that commit SHA.

# Use cases:
# - Vendored dependencies in pre-package-manager days
# - Monorepo-lite: many repos sharing a piece
# - Theme + extension separation

# In 2026: usually a sign you should use a package manager or monorepo tool instead.

# ===== Add =====
git submodule add https://github.com/example/libfoo.git vendor/libfoo
# Creates .gitmodules + adds vendor/libfoo as a submodule reference

# Commit the parent:
git add .gitmodules vendor/libfoo
git commit -m 'add libfoo submodule'

# ===== Clone with submodules =====
git clone --recurse-submodules https://github.com/me/my-app.git
# OR after a regular clone:
git submodule update --init --recursive

# ===== Update submodule to its remote =====
cd vendor/libfoo
git fetch
git checkout v1.2.0
cd ../..
git add vendor/libfoo
git commit -m 'bump libfoo to v1.2.0'

# Or one-liner from parent:
git submodule update --remote vendor/libfoo

# ===== After someone else updates submodules =====
git pull
git submodule update --init --recursive

# ===== Remove =====
git submodule deinit -f vendor/libfoo
git rm -f vendor/libfoo
rm -rf .git/modules/vendor/libfoo
# Then commit.

# ===== Gotchas =====
# 1. Forgetting --recurse-submodules on clone -> empty submodule directory
# 2. Working in submodule without committing pin -> parent silently drifts
# 3. Conflicts in .gitmodules during merges
# 4. CI without 'recurse submodules' -> build fails
# 5. Detached HEAD inside submodules by default -> commits not pointing anywhere

# ===== Alternatives =====
# - npm / pip / cargo / go modules: actual dependency managers
# - Git subtree: merges history into the parent repo
# - Monorepo tools: Nx, Turborepo, Bazel, Pants
# - Git LFS for large files

# ===== When submodules win =====
# - Truly different release cadence for the inner repo
# - Shared between many projects with strict pin
# - You want to keep the submodule's git history separate
# - No suitable package manager (rare)

# ===== When submodules hurt =====
# - Frequent updates from many devs (merge conflicts)
# - Submodule has dependencies of its own (chains of pain)
# - Team unfamiliar with git internals
# - CI / CD pipelines that don't recurse

# ===== Useful commands =====
git submodule status                  # show current commits
git submodule foreach git pull origin main
git config --global submodule.recurse true   # auto-recurse on pull / clone / checkout

# ===== Patterns =====
# - Document the bump policy in README
# - .gitmodules committed, but submodule pins reviewed in PR
# - Pin to immutable refs (tags, sha) not branches
# - CI: clone with --recurse-submodules; update --init --recursive

# ===== Pitfalls =====
# - submodule.recurse = false default -> people forget
# - Working inside submodule without pushing first -> CI breaks for teammates
# - Branch-tracking submodules drift between machines
# - Detached HEAD changes lost without commit

Why it matters

Submodules pin a specific commit of another repo into yours. Useful for truly independent inner repos; painful for fast-moving shared code. Prefer package managers or monorepo tools where they fit. When you must use submodules, document the bump policy and configure submodule.recurse globally.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
git submodule add https://github.com/user/lib lib
git submodule update --init --recursive
Try it Yourself »

Discussion

Loading…