How to Use Git Subtrees
Git repositories inside of other Git repositories

There is no native subtree command, and it is like an approach that allows nesting one repository inside another one as a subdirectory. It is one of the ways to track the history of software dependencies.
Git subtree is the main alternative to Git submodule. However, subtrees should not be confused with submodules. Git subtree is a copy of a Git repository pulled into a parent one. Git submodule is a pointer to a specific commit in another repository. Unlike submodules, subtrees do not need.gitmodules
files or gitlinks
in the repository.
Pros and Cons
Git subtree sometimes has advantages over submodules, and there are a bunch of pros that you should encounter when choosing between them. However, there are some cons, too.
Pros
- Supported by the older version of Git. It can handle even the versions older than 1.5.2.
- Simple workflow management
- Available sub-project code after the super-project is done
- No new Git knowledge
- Modification of content without creating a separate repository copy of the dependency
Cons
- Learning a new merge strategy
- Complicated contributing code back upstream for the sub-projects
- Not mixing super and sub-project code
How to Use Git Subtree
Let’s discuss a short scenario. Suppose there is an external project, and you want to add it to your repository. For instance, to add a vim
extension in a repository that stores your vim
setup, you should act like this:
git subtree add — prefix .vim/bundle/example https://github.com/Example/vim-example.git master — squash
The given example will squash the whole history of the vim-example
project into your folder .vim/bundle/example
, recording the SHA-1 of the master at the time for future reference.
commit 6d7054b3gcea64e2e31f4d6fb2e3be12e5865e87
Merge: 87fa91e ef86deb
Author: Bob Brown<bob@brown.com>
Date: Tue Jun 23 13:37:03 2020 +0200
Merge commit ‘fe67ddf158faccff4082d78a25c45d8cd93e8ba8’ as ‘.vim/bundle/example’
commit fe67ddf158faccff4082d78a25c45d8cd93e8ba8
Author: Bob Brown<ann@brown.com>
Date: Tue May 22 13:37:03 2019 +0200
Squashed ‘.vim/bundle/example/’ content from commit b999b09
git-subtree-dir: .vim/bundle/example
git-subtree-split: b999b09cd9d69f359fa5668e81b09dcfde455cca
If you call Git status, you won’t see anything, as Git subtree will have created the commits for you and left the working copy clean. Also, there will be nothing in the vim/bundle/example
to indicate that the folder ever came from another repository.
Pulling
To update the subfolder to the latest version of the child repository, just run:
git subtree pull — prefix .vim/bundle/example https://github.com/Example/vim-example.git master — squash
Notice that the parameters remain the same, only the git add
command is replaced with the git pull
command, which will create a similar set of commits to the earlier add.
But keep in mind that Git subtree stores the IDs of the subproject commit, not the references in the metadata. Find the symbolic name that is connected with a commit:
git ls-remote https://github.com/Example/vim-example.git | grep <sha-1>
Rebasing After a Git Subtree
Here, you should manually do an interactive rebase and remove the add
commits, then execute rebase. Continue and re-execute the add
command after the rebase process is completed.
Git subtree is easier for Git beginners. The code is merged in the history of the outer repository. It just requires clone
, pull
, and push
, and is decentralized. Git subtree allows you to integrate another repository in your repository, including the history. That is why, after integrating it, the size of your repository becomes bigger. There isn’t a connection to the other repository after the integration, and you don’t need access to it unless you update it. Though it is considered as a great alternative to Git submodule, each of them is designed for different workflows.