Skip to main content
Submodules and subtrees are Git tools that allow subprojects to be included as a subdirectory within a project. The implementation of each is very different.
Submodules maintain a pointer to a specific commit in the external repository, while subtrees copy the entire subproject history (or a squashed version) into the parent repository.

Adding a New Subproject

git submodule add https://github.com/githubtraining/example-submodule
git commit -m "adding new submodule"
The submodule add command adds a new file called .gitmodules along with a subdirectory containing the files from example-submodule. Both are added to your index (staging area) and you simply need to commit them. The submodule’s history remains independent of the parent project.

Viewing a Diff of the Subproject

# show changes to the submodule commit
git diff example-submodule

# show oneline log of new commits in the submodule
git diff --submodule example-submodule

# show changes to the files in the submodule
git diff --submodule=diff

Cloning a Repository with a Subproject

To clone a repository along with its submodules:
git clone --recurse-submodules URL
If you forgot --recurse-submodules, you can clone and initialize all submodules:
git submodule update --init --recursive
Adding --recursive is only required if any submodule itself has submodules.

Pulling in Superproject Updates

By default, the submodule repository is fetched, but not updated when you run git pull in the superproject. You need to use git submodule update, or add the --recurse-submodules flag to pull:
git pull
git submodule update --init --recursive

# or, in one step (Git >= 2.14)
git pull --recurse-submodules
--init is required if the superproject added new submodules, and --recursive is needed if any submodule itself has submodules.If ever the superproject changes the URL of the submodule, a separate command is required:
# copy the new URL to your local config
git submodule sync --recursive

# update the submodule from the new URL
git submodule update --init --recursive
--recursive is only needed if any submodule itself has submodules.

Changing Branches

By default, the submodule working tree is not updated to match the commit recorded in the superproject when changing branches. You need to use git submodule update, or add the --recurse-submodules flag to switch:
git switch <branch>
git submodule update --recursive

# or, in one step (Git >= 2.13)
git switch --recurse-submodules <branch>

Pulling in Subproject Updates

# Update the submodule repository
git submodule update --remote

# Record the changes in the superproject
git commit -am "Update submodule"
If you have more than one submodule, you can add the path to the submodule at the end of the git submodule update --remote command to specify which subproject to update.By default, git submodule update --remote will update the submodule to the latest commit on the main branch of the submodule remote.You can change the default branch for future calls with:
# Git >= 2.22
git submodule set-branch other-branch

# or
git config -f .gitmodules submodule.example-submodule.branch other-branch

Making Changes to a Subproject

In most cases, it is considered best practice to make changes in a separate clone of the subproject repository and pull them in to the parent project. When this is not practical, follow these instructions:
Access the submodule directory and create a branch:
cd example-submodule
git switch -c branch-name main
Changes require two commits, one in the subproject repository and one in the parent repository. Don’t forget to push in both the submodule and the superproject!

Pushing Changes to the Subproject Repository

While in the submodule directory:
git push
Or while in the parent directory:
git push --recurse-submodules=on-demand

Helpful Configs for Submodules

These configuration settings can make working with submodules more convenient: Always show the submodule log when you diff:
git config --global diff.submodule log
Show a short summary of submodule changes in your git status message:
git config --global status.submoduleSummary true
Make push default to --recurse-submodules=on-demand:
git config --global push.recurseSubmodules on-demand
Make all commands (except clone) default to --recurse-submodules if they support the flag (this works for git pull since Git 2.15):
git config --global submodule.recurse true

Build docs developers (and LLMs) love