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
- 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
- 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
- Fetch the git repo into the git-svn one:
cd repo-remote git fetch ../repo
- Then go to the new tree and make it into a branch:
git checkout FETCH_HEAD git branch tomergeTEMP git checkout master
- 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-SHA1and the second
- Put those into a graft file for git-svn to use:
echo repo-SHA1 repo-remote-SHA1 > .git/info/grafts
- Rebase the git-svn repo:
git rebase tomergeTEMP
- 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
- 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
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!
- Which is bizarre considering that I successfully committed it earlier. [↩]