Git Subtrees
I’ve been working on a Spree project recently and decided to open source a couple of the extensions about half way through development. After doing a little reading here, here and here I decided a better fit would be to use a subtree.
I broke the extensions off into their own repos, forked them to my team account, and began the process of re-integrating them into the project as subtrees using the following example comes from here which assumes we are installing Rack as a subtree.
The article was a little verbose so I’ve broken the steps down into more copy-paste-friendly format:
Subtrees
While in master
branch
$ git remote add rack_remote git@github.com:schacon/rack.git
$ git fetch rack_remote
$ git checkout -b rack_branch rack_remote/master
$ git read-tree --prefix=rack/ -u rack_branch
Everything from rack_branch
should now be staged
$ git commit -m 'your-commit-message'
$ git push
Make sure it all worked correctly:
$ git checkout rack_branch
$ git pull
"everything up-to-date"
$ git push
"everything up-to-date"
**NOTE** I’m doing a test push/pull here to make sure rack_remote
only retains a master
branch when we push. At first, I accidentally typed git checkout -b rack_branch rack_remote
(instead of rack_remote/master
) and created rack_remote/rack_branch
when I pushed, however, after we push there should still only be a master
branch in our subtrees.
So, remotes should now include :
-
origin/master
-
whatever other branches you have pointing to
origin
-
rack_remote/master
Caveats
Merging
-
specify the subtree merge strategy with
-s subtree
. -
prevent a bajillion commits from the subtrees from being merged into your history with
--squash
-
stop short of committing so you can add your own commit message (because merge automatically commits) with
--no-commit
your new merge command should look like:
$ git merge --squash -s subtree --no-commit rack_branch
Running diffs
trying a git diff
from the master branch against the rack_branch will result in every file not present in the rack_branch being shown because, obviously, your entire project in master
doesn’t exist in rack_remote
. Instead, use git diff-tree -p rack_branch
to do a diff between a subtree and it’s remote use git diff-tree -p rack_remote/master
Comments
Thanks for this very useful tutorial! One question: the diff commands don’t seem to work properly for me (on git 1.8.4). Instead, running
git diff-tree -p rack_branch
just shows a diff of the last commit made to rack_branch (rather than doing any kind of comparison of changes made in master to rack_branch).
Do you know if this is a mistake? Or does it work correctly for you?