Bubble Foundry

Tips for merging a Git repository into an SVN one

by Peter.

Recently I finished a project that I’ve been working on and wanted to delivery it to the client. Unfortunately, I’ve been using Git, while they wanted me to commit the code into their monolithic Subversion repository. Of course the easiest thing would be to simply commit an export of the project into a new directory, but that would involve losing a lot of important information contained in the commits and their commit messages.

The solution, then, is some git-svn magic. I found most of the tips in one helpful article, but I still had enough gotchas that I figure it’s worth outlining my process here.

I’m assuming standard branch, tag, trunk organization for both the git and svn repos. We’re going to be working in a directory that initially only contains the git-tracked code in a subdirectory called repo.

      1. Move all files in your git repo into the desired subdirectory:
        cd repo
        git mv [all files] myproject

        What I’m doing here is preparing my code for life in the monolithic svn repo, where we’ll want it under a subdirectory, here named myproject.

      2. Clone the trunk of the svn repo:
        cd ..
        git svn clone svn://server/repo/trunk repo-remote

        Here I’m cloning the trunk of the svn repo into a git-svn repo called repo-remote.

      3. Fetch the git repo into the git-svn one:
        cd repo-remote
        git fetch ../repo
      4. Then go to the new tree and make it into a branch:
        git checkout FETCH_HEAD
        git branch tomergeTEMP
        git checkout master
      5. Now we’re going get the SHA1 hashes needed to graft the two trees:
        git rev-list --reverse FETCH_HEAD  | head -n 1
        git rev-list --reverse HEAD  | head -n 1

        Let’s call the first hash repo-SHA1 and the second repo-remote-SHA1.

      6. Put those into a graft file for git-svn to use:
        echo repo-SHA1 repo-remote-SHA1 > .git/info/grafts
      7. Rebase the git-svn repo:
        git rebase tomergeTEMP
      8. Now do a dcommit dry run:
        git svn dcommit --dry-run

        You’ll get a bunch of lines that look like:

        Committing to svn://server/repo/trunk ...
        diff-tree hashA~1 hashA
        diff-tree hashB~1 hashB

        All the articles just give the line above and say to check the patches, but how do you actually check them? It turns out it’s quite simple:

        git diff-tree hashA~1 hashA -u

        This will give you the patch that will be applied. (I’m not sure whether the -u is useful.)

      9. Finally, commit:
        git svn dcommit

        It should go through and commit to the svn repo each git commit.

When it’s finished the code from your git repo should be in svn://server/repo/trunk/myproject.

However, I had a few hiccups. First, I got a merge conflict on one of my commits.1 I resolved it like so:

git mergetool
git add [corrected files]
git rebase --continue
git svn dcommit

You’ll notice that not only did I have to continue the rebase operation that git svn dcommit had called internally, but I actually had to call git svn dcommit again.

Second, I had some unnecessary folders because I had moved my files in and out of some folders (hopefully you won’t have this problem because you followed my first two steps). Rather than try to remove the unncessary folders using my git-svn repo, I checked out a new working copying via svn:

cd ../
svn co svn://server/repo/trunk repo-svn
cd repo-svn
svn rm [extraneous dirs]
svn commit -m "Remove directories incorrectly left by git-svn"

And there you go. Hope that helps!

  1. Which is bizarre considering that I successfully committed it earlier. []