Skip to content

Git Submodules & Subtrees: Managing Dependencies Across Repos

DodaTech Updated 2026-06-22 5 min read

In this tutorial, you'll learn about Git Submodules & Subtrees: Managing Dependencies Across Repos. We cover key concepts, practical examples, and best practices.

Git submodules embed a specific commit from another repository as a subdirectory, while subtree copies the external repository's history into your own.

In this tutorial, you'll learn Git submodules and subtrees — two built-in mechanisms for including external repositories inside your project. Both let you track dependencies from other repositories, but they work very differently. By the end, you'll know when to use each and how to manage them effectively.

flowchart TD
  A[Need external dependency] --> B{How to include?}
  B --> C[Submodule]
  B --> D[Subtree]
  C --> E[Reference to external repo]
  C --> F[Locked to specific commit]
  D --> G[Copied into your repo]
  D --> H[Full history included]
  E --> I[Submodule update fetches]
  G --> J[Subtree pull merges]

Git Submodules: Referencing External Repos

Add a submodule to your project:

git submodule add https://github.com/user/lib.git libs/my-lib
git commit -m "Add my-lib as submodule"

Expected output:

Cloning into 'libs/my-lib'...
remote: Enumerating objects: 50, done.
Receiving objects: 100% (50/50), done.

This creates a .gitmodules file tracking the submodule configuration:

[submodule "libs/my-lib"]
  path = libs/my-lib
  url = https://github.com/user/lib.git

Cloning a Repo with Submodules

When cloning a project that uses submodules:

git clone --recursive https://github.com/user/project.git

Or update submodules after a regular clone:

git clone https://github.com/user/project.git
git submodule update --init --recursive

Updating Submodules

Pull the latest from each submodule's configured branch:

git submodule update --remote
git add .
git commit -m "Update submodules to latest versions"

Git Subtree: Copying External Repos

Add a subtree to include external code:

git subtree add --prefix=libs/my-lib https://github.com/user/lib.git main --squash

Expected output:

git fetch https://github.com/user/lib.git main
Added dir 'libs/my-lib'

Pull updates from the external repo:

git subtree pull --prefix=libs/my-lib https://github.com/user/lib.git main --squash

Submodules vs Subtrees

Feature Submodule Subtree
Reference type Pointer to external commit Copied content
History Stored externally Included in your repo
Clone cost Separate fetch per submodule Single clone, larger repo
Modify from parent Read-only by default Can edit and push back
Team friction Team must learn submodule commands Transparent to team
Atomic dependency updates Update + commit Subtree pull + commit
CI/CD complexity Extra checkout step No extra steps

Working with Submodule Branches

By default, submodules point to a specific commit. Track a branch instead:

# Configure submodule to track a branch
git config -f .gitmodules submodule.libs/my-lib.branch main

# Update to latest commit on that branch
git submodule update --remote

# The .gitmodules entry now shows:
# [submodule "libs/my-lib"]
#   path = libs/my-lib
#   url = https://github.com/user/lib.git
#   branch = main

Subtree Push Back to Upstream

Unlike submodules, subtrees let you push changes back upstream:

# Make changes in libs/my-lib
cd libs/my-lib
echo "new feature" >}} feature.py
cd ../..

# Split the changes back to the external repo
git subtree push --prefix=libs/my-lib https://github.com/user/lib.git main

Common Errors

Error Cause Fix
fatal: not a git repository in submodule Submodule not initialized Run git submodule update --init
No URL configured for submodule Missing from .gitmodules Check .gitmodules file
detached HEAD in submodule Submodule points to commit, not branch git submodule update --remote
Pathspec 'libs/my-lib' is in submodule Git commands can't cross submodule boundary cd into submodule then run commands
Subtree prefix already exists Directory already in use Remove directory or choose different prefix
fatal: refusing to merge unrelated histories Subtree fetch issues Use git subtree pull --squash
Submodule shows as modified Expected behavior Commit the submodule change in parent
Slow clone with many submodules Each submodule cloned separately Use --depth 1 on submodules

Practice Questions

What is a Git submodule?

A Git submodule is a reference to a specific commit in another repository. It embeds an external repository as a subdirectory of your project, locked to a particular commit. Updates require explicit git submodule update commands.

When should I use subtree instead of submodule?

Use subtree when you want the external code to be fully part of your repository — editable by your team, included in clones, and visible in the same history. Use submodule when the dependency changes independently and you want to track specific versions.

How do I update all submodules to their latest commits?

Run git submodule update --remote from the repository root. This fetches the latest commit from each submodule's configured branch. Then commit the updated submodule references in the parent repository.

Can I modify code inside a submodule?

Yes, but it's read-only by default. To modify, cd into the submodule, create a branch, make changes, and push from within the submodule. Other users will see your changes only when the parent's submodule reference is updated.

How do I remove a submodule?

Run git submodule deinit -f libs/my-lib, then git rm -f libs/my-lib, then delete the .gitmodules entry and commit. Without proper deinitialization, the submodule's .git directory remains

Challenge

Create a repository that depends on a public library via submodule. Add the submodule, make a change to how it's used in the parent, update the submodule to a newer version, and commit. Then remove the submodule and re-add the same dependency using subtree. Compare the resulting repository sizes.

Real-World Task

You maintain a shared authentication library in one repository and an application in another. Use Git submodules to include the auth library in the app repo. Set up a CI/CD pipeline that runs submodule initialization during the build. Configure Dependabot-style automated updates using a cron job that runs git submodule update --remote and creates a PR. This mirrors how DodaTech manages shared libraries across DodaZIP and Durga Antivirus Pro.


Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro