Chapter 51: Git Submodules
Git Submodules
This is the part where Git stops feeling like a simple version control system and starts feeling like you’re managing multiple separate Git repositories inside one parent repository.
Many people try submodules once, get frustrated, swear never to use them again, and then… six months later they are forced to use them because nothing else fits their real-world need.
Today I’m going to explain git submodules very clearly, very slowly, very honestly — like I’m sitting next to you creating one together — so that you understand exactly what they are, why they exist, when to use them, when to never use them, and how to survive the most common pain points.
1. What is a Git Submodule? (very simple mental model)
A submodule is a full, separate Git repository that lives inside your main (parent) repository as a normal subfolder.
- The submodule folder contains its own .git folder
- It has its own commit history, branches, tags, etc.
- The parent repo only stores a pointer (a special entry in the index) saying: “At this commit of the parent, the submodule folder should be at this exact commit of the submodule repo”
Think of it like:
- Your main project = a big cookbook you are writing
- A submodule = a borrowed recipe book from a friend that lives inside your cookbook as a chapter
- You don’t copy the entire borrowed book into your cookbook (that would duplicate history)
- You only write a note: “At chapter 5, use recipe book XYZ at page 42”
- When someone reads your cookbook, they go fetch the exact version of the borrowed book you pointed to
2. Why do people use submodules? (real 2026 reasons)
Common situations where submodules are the cleanest (or only) solution:
- Your project depends on another open-source library that is itself a Git repo (and you want to pin to exact commit)
- You maintain multiple related projects (frontend + backend + shared UI components) and want to develop them together
- You have a monorepo but some parts need to be reusable as standalone repos
- You want different teams to own different sub-repos but integrate them into one product
- You need exact version pinning of a dependency that is not published to npm/Maven/PyPI/etc.
3. Realistic Example – Create & Use a Submodule Right Now
Goal: Your main todo app wants to use a shared “ui-components” library that lives in its own separate repo.
Step 1 – Create two separate repos
Shared UI Components repo (the submodule)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mkdir ui-components cd ui-components git init echo "# Shared UI Components" > README.md echo "<button>Click me</button>" > Button.html git add . git commit -m "Initial UI components" git remote add origin https://github.com/your-username/ui-components.git git push -u origin main |
Main Todo App repo (the parent)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
cd .. mkdir todo-app cd todo-app git init echo "# Todo App" > README.md git add README.md git commit -m "Initial todo app" git remote add origin https://github.com/your-username/todo-app.git git push -u origin main |
Step 2 – Add submodule to main repo
Inside todo-app:
|
0 1 2 3 4 5 6 |
git submodule add https://github.com/your-username/ui-components.git components/ui |
What happens:
- Creates folder components/ui
- Clones the ui-components repo inside it
- Adds special entry to .gitmodules file:
|
0 1 2 3 4 5 6 7 8 |
[submodule "components/ui"] path = components/ui url = https://github.com/your-username/ui-components.git |
- Stages both .gitmodules and the submodule pointer
Commit:
|
0 1 2 3 4 5 6 7 |
git commit -m "chore: add ui-components as submodule" git push |
Step 3 – Clone the repo with submodules (very important!)
Someone else (or you on new machine) clones:
|
0 1 2 3 4 5 6 7 |
git clone https://github.com/your-username/todo-app.git cd todo-app |
→ The components/ui folder is empty!
You need extra step:
|
0 1 2 3 4 5 6 7 |
git submodule init git submodule update |
Or all-in-one (recommended):
|
0 1 2 3 4 5 6 |
git clone --recurse-submodules https://github.com/your-username/todo-app.git |
Now components/ui contains the actual UI components repo at the exact commit you pinned.
Step 4 – Update submodule to newer version
Later you want newer version of ui-components:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
cd components/ui git switch main git pull origin main cd ../.. git add components/ui git commit -m "chore: update ui-components to latest" git push |
→ Parent repo now points to new commit of submodule.
4. Most Common Submodule Commands (daily use)
| What you want to do | Command | Notes / When to use |
|---|---|---|
| Add new submodule | git submodule add <url> path/to/folder | Creates folder + .gitmodules entry |
| Clone repo + all submodules | git clone –recurse-submodules <url> | Most common clone command with submodules |
| Initialize + update submodules (after normal clone) | git submodule update –init –recursive | After git clone without –recurse |
| Update all submodules to latest | git submodule update –remote –merge | Pulls latest from submodule remotes |
| Update one submodule | cd submodule-folder; git pull git add .; git commit | Normal Git inside submodule |
| Remove submodule | git submodule deinit path/to/folder git rm path/to/folder remove from .gitmodules | Clean removal |
5. Why People Hate Submodules (honest 2026 reality)
| Pain point | Why it hurts | Modern alternatives / workarounds (2026) |
|---|---|---|
| Submodules are empty after clone | Need extra git submodule update step | Teach team or use –recurse-submodules |
| Updating submodules is manual & error-prone | Must cd into each, pull, then commit pointer in parent | Git submodules foreach scripts or tools like git-submodule-tools |
| History looks weird (two layers) | git log inside submodule vs parent | Accept it or use monorepo tools |
| Hard to move/rename submodules | Breaks paths in .gitmodules | Careful manual edits + commit |
| Large number of submodules = slow operations | Every submodule is separate Git repo | Prefer monorepo (Turborepo, Nx, Bazel) if possible |
2026 verdict — submodules are still used a lot, especially in:
- C/C++/Rust projects
- Embedded systems
- Game development (Unity/Unreal assets)
- Projects that need exact commit pinning of dependencies
- Companies with strict separation of concerns
But many teams now prefer:
- Monorepos (one giant repo with everything)
- Package managers (npm, Cargo, Maven, PyPI) for libraries
- Git subtrees (alternative to submodules — merges history)
6. Quick Git Submodule Cheat Sheet
| Task | Command / Pattern | Notes |
|---|---|---|
| Add submodule | git submodule add <url> path/to/folder | Creates .gitmodules entry |
| Clone + init submodules | git clone –recurse-submodules <url> | One-step clone |
| Update submodules after normal clone | git submodule update –init –recursive | Must do after plain git clone |
| Update all to latest | git submodule update –remote –merge | Pulls latest from each submodule remote |
| Remove submodule | git submodule deinit path git rm path | Also delete from .gitmodules |
| Run command in every submodule | git submodule foreach ‘git pull’ | Very useful for bulk operations |
Got the submodule feeling now?
Git submodules = “nested Git repositories inside your main repo — with exact commit pinning and separate histories”
They are not beautiful, but they solve real problems when you need exact version control of external codebases inside your project.
Next?
- Want to convert a normal dependency folder into a submodule?
- See git submodules vs git subtrees comparison?
- Or do one final big summary of all Git concepts we covered today?
Just tell me — we’ll finish strong. You’ve now gone from zero to very advanced Git — truly impressive work! 🚀
