May 26, 2017 Webby

Subtree merge as an alternative to submodules with git svn

We use Subversion as our version control system for all client work here at Easy because we absolutely love Springloops’ hosted Subversion service, but we use Git for all of our open source projects because, well, Git is a lot more fun to work with and we love the community that’s built up around Github. In order to have the best of both worlds when working on client projects, we use git-svn as our front-end to Subversion. It’s a great tool, but it’s not without its limitations. One such limitation is its inability to translate Git submodules into svn:externals. Thankfully, Git offers an alternative that is comparable and plays nicely with Subversion: the subtree merge.

When attempting to dcommit a Git repository containing a submodule, you’ll likely receive a message like this:

952bee47201e87b0b0e851bcbe6c8940d429cda0 doesn’t exist in the repository at /usr/local/git/libexec/git-core/git-svn line 3787 Failed to read object 952bee47201e87b0b0e851bcbe6c8940d429cda0 at /usr/local/git/libexec/git-core/git-svn line 480

That annoying message is the painful reminder that you need to find another way to add content from another project into your repository. Subtree merge to the rescue!

If you’ve already hit the error, go ahead and delete your submodule folder(s) and the .gitmodules file and commit the changes to your repository to make the path available again. Next, from a shell within the root of your Git repository enter these commands at the prompt (replacing the capitalized phrases with your relevant information):

  1. git remote add -f LOCAL_NAME PATH/TO/GIT/REPOSITORY
  2. git merge -s ours --no-commit LOCAL_NAME/BRANCH_NAME
  3. git read-tree --prefix=PATH/I/WANT/IT/IN/ -u LOCAL_NAME/BRANCH_NAME
  4. git commit -m "Merge of PROJECT"
  5. git pull -s subtree LOCAL_NAME master

To provide a fully fleshed-out example for you, I used the following to merge the master branch of eCSStender into the path vendors/ecsstender within another project.

  1. git remote add -f eCSStender git://github.com/easy-designs/eCSStender.js.git
  2. git merge -s ours --no-commit eCSStender/master
  3. git read-tree --prefix=vendors/ecsstender/ -u eCSStender/master
  4. git commit -m "Merge of eCSStender into the vendors directory"
  5. git pull -s subtree eCSStender master

The beauty of this is that you can use that last line to pull in the latest version of the external project and then all you have to do is dcommit the changes to get them into Subversion. Problem solved.