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

    Klinton |

    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?