This is an alternative to submodules:
This gives us a way to have a workflow somewhat similar to the submodule workflow without using submodules (which we will cover in Submodules). We can keep branches with other related projects in our repository and subtree merge them into our project occasionally. It is nice in some ways, for example all the code is committed to a single place. However, it has other drawbacks in that it’s a bit more complex and easier to make mistakes in reintegrating changes or accidentally pushing a branch into an unrelated repository.
Say you want to merge repository a
into b
. This can be accomplished with one command:
git subtree add --prefix=a ../a master
This produces the following history (commit names reflect names of the files that were added):
$ git log --oneline --graph --decorate --all
* 97d548a Add 'a/' from commit 'b1630a6'
|\
| * b1630a6 ad1/a2
| * 0e8e0d3 a1
* 3b75042 bd1/b2
* 4b2b43b b1
And the following tree:
r
├── b1
├── bd1
│ └── b2
└── a
├── a1
└── ad1
└── a2
But in the merged commits the files are still at the root, so:
$ git log -p --name-status --oneline a/a1
97d548a Add 'a/' from commit 'b1630a6'
$ git log -p --name-status --oneline --follow -- a1
0e8e0d3 a1
A a1
If you later change say a/a1
, then git log a/a1
will show you the change + the merge commit. And git log --follow -- a1
will show you only the change from the merged history. None of them will show you all the changes.
Later, to pull in changes from repository a
you need:
git pull -s subtree a master
As a result you get:
$ git log --oneline --graph --decorate --all
* f4469a5 (HEAD -> master) Merge branch 'master' of ../a
|\
| * 367e02f (a/master) a3
* | 97d548a Add 'a/' from commit 'b1630a6'
|\|
| * b1630a6 ad1/a2
| * 0e8e0d3 a1
* 3b75042 bd1/b2
* 4b2b43b b1
A more wordy way of doing git subtree
is as follows.
First you add a remote:
git remote add -f a ../a
-f
makes it autofetch the remote.
Then attach the a
's history, but don't make a commit:
git merge -s ours --no-commit --allow-unrelated-histories a/master
-s ours
makes it not apply the changes, as that would apply them to the root of the repository (not to a subdirectory). --allow-unrelated-histories
is needed since git >= 2.9
.
Then apply changes to a subdirectory:
git read-tree --prefix=a -u a/master
-u
makes it apply them not only to index, but also to the working directory.
And commit the changes:
git commit -m "Subtree merged in a"
About Git subtree merges
How to use the subtree merge strategy
Subtree Merging
Merging git repositories, putting one of them into a subdirectory