🧩 Git Submodules Explained — Complete Guide with Best Practices, Tips & Tricks

Have you ever worked on a large project where multiple repositories needed to stay in sync — like a shared API, Terraform module, or a reusable microservice?
If yes, then Git submodules might just become your new best friend. 💡
In this post, we’ll dive deep into:
What Git submodules are
How they work internally
Best practices and common pitfalls
Useful commands, tips, and tricks
🚀 What Are Git Submodules?
A Git submodule allows you to include one Git repository inside another as a subdirectory.
Each submodule keeps its own commit history, branches, and identity — but still links to the parent project.
Think of it like this 👇
“A submodule is a Git repo inside another Git repo — linked at a specific commit.”
This is useful when you have shared components across multiple projects, like:
A shared library used in multiple apps
A common DevOps configuration (Jenkinsfile, Dockerfiles, etc.)
A reusable Terraform module
A frontend design system used by multiple teams
🧠 How Git Submodules Work Internally
When you add a submodule, Git doesn’t copy all its files into your repo.
Instead, it:
Clones the external repository at a specific commit
Stores the reference (commit hash and path) inside a
.gitmodulesfile
That .gitmodules file acts like a map between the parent repo and its submodules.
Example:
git submodule add https://github.com/example/library.git libs/library
This creates:
.libs/
└── library/
.gitmodules
And your .gitmodules might look like this:
[submodule "libs/library"]
path = libs/library
url = https://github.com/example/library.git
⚙️ Common Git Submodule Commands
Here are the most important commands to master:
| Command | Description |
git submodule add <repo-url> <path> | Add a new submodule |
git clone --recurse-submodules <repo> | Clone repo with submodules |
git submodule init | Initialize submodules |
git submodule update | Fetch the submodule content |
git submodule update --init --recursive | Initialize and update all nested submodules |
git submodule foreach git pull origin main | Pull latest code for all submodules |
git submodule deinit <path> | Remove a submodule |
🧩 Example Workflow: Adding and Updating a Submodule
Let’s see it in action 👇
Step 1: Add the submodule
git submodule add https://github.com/example/library.git libs/library
Step 2: Initialize & update
git submodule update --init --recursive
Step 3: Commit the configuration
git add .gitmodules libs/library
git commit -m "Added library as submodule"
Step 4: Update the submodule to latest version
cd libs/library
git checkout main
git pull origin main
cd ../..
git add libs/library
git commit -m "Updated library submodule"
✅ Best Practices for Working with Git Submodules
Submodules can be tricky if used incorrectly. Here’s how to use them the right way 👇
1️⃣ Keep Submodules Read-Only
Treat submodules as dependencies, not active development folders.
If you need to modify them, do it in their own repo — then pull the updated commit in your parent project.
2️⃣ Always Commit Submodule Updates
When you update the submodule’s code, Git only changes its pointer (the commit hash).
So remember to commit that change in the parent repo:
git add libs/library
git commit -m "Update submodule to latest commit"
3️⃣ Use Specific Commits or Tags
Avoid tracking branches like main directly.
Use fixed commits or version tags for stable, predictable builds.
git submodule update --remote --merge
This ensures your parent repo always points to a known, stable version.
4️⃣ Keep Paths Clean and Consistent
Store all submodules in a common folder like /libs or /modules.
This makes your repo structure predictable and readable for others.
5️⃣ Use .gitmodules Wisely
Ensure .gitmodules uses relative URLs if your repos are hosted in the same organization.
This makes cloning easier, especially in private or on-prem environments.
Example:
url = ../shared-library.git
⚡ Pro Tips & Tricks
💡 Tip 1: Clone Everything in One Go
When cloning a repo with submodules, always use:
git clone --recurse-submodules <repo-url>
Otherwise, submodules will be empty until you initialize them manually.
💡 Tip 2: Automate Submodule Updates in CI/CD
Add this in your pipeline to ensure submodules are updated before build:
git submodule update --init --recursive
💡 Tip 3: Use Git Aliases for Convenience
Set up handy aliases in your ~/.gitconfig:
[alias]
smu = submodule update --init --recursive
smp = submodule foreach git pull origin main
Now you can just run git smu or git smp.
💡 Tip 4: Prefer Git Subtree for Heavy Collaboration
If your team frequently edits both parent and submodule repos together, consider using Git subtree instead — it simplifies merges and version tracking.
⚖️ When to Use (and When Not to)
✅ Use Git Submodules When:
You have shared codebases or versioned dependencies
You want to keep histories separate
You need reproducible builds (specific versions locked)
🚫 Avoid When:
The submodule changes frequently alongside the parent repo
Multiple teams edit both repos often
You need complex merge workflows
In such cases, monorepos or Git subtrees might be better options.
🧭 Final Thoughts
Git submodules are powerful, but they require a disciplined approach.
Used wisely, they help you:
Keep dependencies versioned
Maintain modularity
Avoid duplication across projects
But used carelessly, they can lead to detached commits, broken builds, and headaches 😅
So remember:
“Submodules are great for sharing stable, reusable code — not for daily-changing dependencies.”
💬 Conclusion
Mastering submodules can make your Git workflow modular, reusable, and scalable — especially in DevOps and microservice environments.
Start small: try adding a shared script repo or library as a submodule and explore how it integrates smoothly with your parent project.
✍️ Written by Tathagat Gaikwad
🔖 #Git #DevOps #VersionControl #GitTips #SoftwareEngineering




