TechLead
Lesson 7 of 9
5 min read
Advanced Git

Submodules & Subtrees

Manage dependencies and nested repositories with Git submodules and subtrees.

Git Submodules

Submodules allow you to include one Git repository inside another as a subdirectory, while keeping their histories separate.

Adding Submodules

# Add a submodule
git submodule add https://github.com/user/lib.git libs/lib

# Add at specific branch
git submodule add -b main https://github.com/user/lib.git libs/lib

# Initialize after clone
git clone --recurse-submodules https://github.com/user/repo.git

# Or initialize separately
git clone https://github.com/user/repo.git
git submodule init
git submodule update

Working with Submodules

# Update all submodules
git submodule update --remote

# Update specific submodule
git submodule update --remote libs/lib

# Fetch and merge submodule changes
cd libs/lib
git fetch
git merge origin/main
cd ../..
git add libs/lib
git commit -m "Update lib submodule"

# Pull with submodules
git pull --recurse-submodules

Submodule Configuration

# .gitmodules file
[submodule "libs/lib"]
    path = libs/lib
    url = https://github.com/user/lib.git
    branch = main

# Configure to always update
git config --global submodule.recurse true

# Set submodule to track branch
git config -f .gitmodules submodule.libs/lib.branch main

Removing Submodules

# Remove submodule
git submodule deinit libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib
git commit -m "Remove lib submodule"

Submodule Status

# Check submodule status
git submodule status

# Show submodule summary
git submodule summary

# Show submodule diff
git diff --submodule

Git Subtrees

Subtrees merge another repository into a subdirectory of your project, keeping all history in one repository.

Adding Subtrees

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

# Without squash (keeps full history)
git subtree add --prefix=libs/lib https://github.com/user/lib.git main

Updating Subtrees

# Pull updates from subtree remote
git subtree pull --prefix=libs/lib https://github.com/user/lib.git main --squash

# Add remote for convenience
git remote add lib https://github.com/user/lib.git

# Then pull
git subtree pull --prefix=libs/lib lib main --squash

Contributing Back to Subtree

# Push changes back to subtree repository
git subtree push --prefix=libs/lib lib main

# Or create a branch
git subtree split --prefix=libs/lib -b lib-changes
git push lib lib-changes:main

Subtree Merge Strategy

# Alternative: Use merge strategy
git remote add lib https://github.com/user/lib.git
git fetch lib
git merge -s subtree --allow-unrelated-histories lib/main

Submodules vs Subtrees

Submodules Subtrees
Separate repositories Single repository
Requires extra commands Works with standard Git
Points to specific commit Merges full history
Smaller repo size Larger repo size
More explicit dependency Simpler for contributors

When to Use Which

Use Submodules when:
- Dependency is actively developed separately
- You need to track specific versions
- Repo size is a concern
- Team is comfortable with submodule workflow

Use Subtrees when:
- You want simpler workflow
- Contributors don't need to know about dependencies
- You're vendoring a library
- You rarely need to update the dependency

Practical Example: Monorepo with Subtrees

# Create monorepo structure
mkdir monorepo && cd monorepo
git init

# Add existing projects as subtrees
git subtree add --prefix=packages/frontend ../frontend main
git subtree add --prefix=packages/backend ../backend main
git subtree add --prefix=packages/shared ../shared main

# Structure:
# monorepo/
# ├── packages/
# │   ├── frontend/
# │   ├── backend/
# │   └── shared/
# └── .git/

Continue Learning