Git Submodules
Learn what Git submodules are, how to add and update them, and when alternatives like subtree or package managers may be easier.
What Are Git Submodules?
A Git submodule lets one repository include another repository at a specific commit. The parent repository stores a reference to the child repository and exact commit to check out.
The parent project tracks a pointer.
Common use cases include shared libraries, external repositories, and components owned by another team.
Tip: Use submodules when the nested code really needs its own repository lifecycle. If you mainly want easy dependency installation, a package manager is often a better fit.
When to Use Submodules
Submodules make sense when several projects consume the same repository at different versions, ownership stays separate, and you want exact commit-level control.
Adding a Submodule
Use git submodule add with a repository URL and path:
git submodule add https://github.com/example/shared-lib.git libs/shared-lib
This normally creates a .gitmodules file and a gitlink entry that records the submodule commit.
Example .gitmodules content:
[submodule "libs/shared-lib"]
path = libs/shared-lib
url = https://github.com/example/shared-lib.git
Commit both changes in the parent repo:
git add .gitmodules libs/shared-lib
git commit -m "Add shared-lib as a submodule"
Cloning with Submodules
A normal clone does not always fetch submodule contents automatically. The safest approach is:
git clone --recurse-submodules https://github.com/example/platform.git
If you already cloned the parent repository without that flag, initialize submodules afterward:
git submodule update --init --recursive
Note: This is one of the most common submodule mistakes. New contributors often clone the repository, see an empty nested folder, and think the repository is broken.
Updating Submodules
There are two common update patterns.
Syncing to the Parent Repository's Expected Version
If the parent repository changed its recorded submodule pointer, pull the parent changes and sync the submodule checkout:
git pull origin main
git submodule update --init --recursive
Moving the Submodule to a New Commit
If you want to advance the submodule itself:
cd libs/shared-lib
git switch main
git pull origin main
Then go back to the parent repository and commit the new pointer:
cd ../..
git add libs/shared-lib
git commit -m "Update shared-lib submodule"
Updating the child repo is not enough. The parent repository must record the new submodule commit too.
Helpful Commands
Useful inspection commands:
git submodule status
git diff --submodule
git status
Removing a Submodule
Submodule removal is more involved than deleting a normal directory.
A common sequence is:
git submodule deinit -f libs/shared-lib
git rm -f libs/shared-lib
rm -rf .git/modules/libs/shared-lib
Then commit the cleanup:
git add .gitmodules
git commit -m "Remove shared-lib submodule"
Common Pitfalls
Forgetting --recurse-submodules
This leads to missing contents after clone.
Detached HEAD Confusion
Submodules often check out a specific commit rather than following a branch tip automatically.
Pointer Drift
A developer updates the submodule repository but forgets to commit the new pointer in the parent repository. Teammates then keep getting the old version.
CI and Onboarding Friction
Automation must initialize submodules properly.
Alternatives to Submodules
Git Subtree
Git subtree brings another repository into a subdirectory, but the content is stored directly in the parent repository history.
Advantages include easier cloning and no separate initialization step. The trade-off is that syncing changes back can be less obvious.
Package Managers
For many dependencies, package managers are simpler than submodules.
Examples:
npmfor JavaScriptpipfor Python- Go modules for Go
Package managers usually offer easier onboarding for standard libraries.
What You Should Remember
Submodules let a parent repository track another repository at a specific commit. They are useful for shared libraries and external repos that need independent history, but they add complexity around cloning, updating, CI, and removal. Learn the core commands, watch out for pointer updates, and remember that git subtree or a package manager may be easier for many projects.
Test Your Understanding
Exercise 1: Understanding what the parent repo stores
What does the parent repository primarily track for a submodule?
Exercise 2: Cloning correctly
Which command is the safest way to clone a repository and fetch all of its submodules in one step?
Exercise 3: Choosing an alternative
When might a package manager be a better choice than a Git submodule?