How to convert from Subversion to Git

When I was moving EC2 on Rails to Git I found several posts that explained how to convert a repo from svn to Git. But none of them included converting your svn tags to Git tags, so here’s yet another how-to guide. (Git experts please comment if I’m doing anything dumb.)

1. Install Git

First, you’ll need Git installed with git-svn included (git-svn will actually allow you to push changes back to the original Subversion repository, but for our purposes we’re assuming that this is a one-time conversion).

If you’re using OS X you should already be using MacPorts, so just do:

prompt> sudo port install git-core +svn

Or, on Ubuntu or Debian Linux:

prompt> sudo apt-get install git-svn

2. Create the authors file

Next, create a text file that maps Subversion committers to Git authors so the names and email addresses will be correct in the history. Save it as authors.txt:

pdowman = Paul Dowman <paul@hellospambot.com>
svnuser2 = Another User <anotheruser@whatever.com>

3. Clone the repository

Now run the command that will import your svn repo into a local Git repo. I’m assuming your svn repo had the standard layout of /trunk, /tags and /branches.

prompt> git svn clone <svn repo url> --no-metadata -A authors.txt -t tags -b branches -T trunk <destination dir name>

Now running git log should show all your commit history with the correct authors.

4. Convert branches to tags

There’s one more thing. All your tags are now remote branches, not tags, in your Git repo. So you’ll need to convert them manually (or write a script to do it if you have a lot, I’ll leave that as an exercise for the reader). For each Subversion tag (i.e. Git remote branch) you’ll add it as a Git tag, then delete the remote branch. List them with:

promp> git branch -r

Then for each tag listed do:

prompt> git tag tagname tags/tagname
prompt> git branch -r -d tags/tagname

You now have a local Git repository with all your history and tags. If you don’t need to share it with anyone else then you’re done.

5. Push to a public repo (optional)

If you want to publish to a public repository (for example Github), you’ll need to add it as a remote repo and then push to it.

prompt> git remote add origin git@github.com:userid/project.git
prompt> git push origin master --tags

You next stop should probably be the Git tutorial for Subversion users. Enjoy!

17 Comments

  1. Gianni Chiappetta:

    Thanks Paul, I’ll be referring to this article!

  2. floehopper:

    Thanks - that’s helpful. In case anyone else finds it useul, here’s a quick one-liner to list all the users that have committed to your svn repository…

    svn log –quiet | grep ‘^r.*’ | cut -d ‘ ‘ -f 3 | sort | uniq

  3. Martin:

    floehopper: I make that
    svn log –quiet | grep ^r | cut -d ‘|’ -f 2 | sort | uniq

  4. Ron Evans:

    For me, I use:

    svn -q log | grep ^r | cut -d ‘|’ -f 2 | sort | uniq

  5. Ron Bieber:

    Quick and dirty shell script to convert remote tags/branches to tags per this article:

    #!/bin/sh
    for i in `git branch -r|grep tags\/|grep -v \\@`
    do
    TAGNAME=`echo $i | perl -p -e ’s/tags\///sig;’`
    echo “Converting $i to $TAGNAME”;
    git tag $TAGNAME $i
    git branch -r -d $i >/dev/nul
    done

  6. matt:

    Thanks for this explanation. For the most part, very helpful. Only got stuck on step 3, as there are some parameters missing.

    Intead of:
    git svn clone –no-metadata -A authors.txt -t tags -b branches -T trunk

    it would be clearer to indicate:
    git svn clone [svn repository] –no-metadata -A authors.txt -t tags -b branches -T trunk [destination]

    other than that, very helpful!

  7. Paul Dowman:

    Thanks Matt, I’ve fixed it.

  8. andreas:

    Step 4 isn’t working on my machine.
    I’ve got the listing of the remote branches:
    git branch -r
    Ver2.x
    tags/Ver1.0
    tags/Ver1.1
    tags/Ver2.0
    trunk

    But the git tag only tags in the remote branch. After deleting the remote branch I end up with no tags in my local clone. Am I missing somthing?

    The thing is I want to totally convert to git, no step back. So I want to have all remote branches in the local master.

  9. Shaun Mangelsdorf:

    andreas:
    My best guess without any more information than you’ve given here is that you have committed to your tags as though they were branches. When you do that, there is an extra changeset (or more than one) that is unique to the svn tag, so git needs to treat it as a branch.

    To revert this behaviour, you can do something like this:

    $ git merge-base trunk tags/Ver1.0
    abcdef1234567890abcdef1234567890abcdef12
    $ git tag Ver1.0 abcdef1234567890abcdef1234567890abcdef12
    $ git branch -r -d tags/Ver1.0

    Note though that you’ll likely lose revisions if you do this. If you want to see what revisions you’ll lose:
    $ git merge-base trunk tags/Ver1.0
    abcdef1234567890abcdef1234567890abcdef12
    $ git log abcdef1234..trunk

    Note I’ve used a shortened form of the revision hash, two periods, and then “trunk” to specify the range. (Just making it obvious that it’s to be taken literally :)

  10. Dustin:

    I haven’t gotten this to work yet, but it seems step 3 should be [svn repo url] rather than [git repo url], right?

  11. Paul Dowman:

    Oops, thanks Dustin! I’ve fixed it.

  12. Jason L:

    FYI - for the “authors.txt” file, the blog post ate the email part of it (because of the greater-than less-than brackets). It should be:

    username = John Doe (less-than bracket)some.email@example.com(greater-than bracket)

    Thanks for the article!

  13. Paul Dowman:

    Thanks Jason, I’ve fixed that. As you said, the formatting was messed up, it had actual angle-bracket characters instead of HTML entities.

  14. Rodrigo:

    I’m getting the following error on step 3
    Found possible branch point: http://svn2.xp-dev.com/svn/oncemade-cssSys/trunk => http://svn2.xp-dev.com/svn/oncemade-cssSys/tags/ModernBrowsers, 62
    Use of uninitialized value in substitution (s///) at /opt/local/libexec/git-core/git-svn line 1657.
    Use of uninitialized value in concatenation (.) or string at /opt/local/libexec/git-core/git-svn line 1657.
    refs/remotes/trunk: ‘http://svn2.xp-dev.com/svn/oncemade-cssSys’ not found in ”

    how can I fix it?

    Thanks!

  15. W-Mark Kubacki:

    If you have more than one repository, this loop might help you finding all users:

    for R in repo1 repo2 repo3 …; do svn log –quiet file:///var/svn/$R | grep ^r | cut -d ‘|’ -f 2; done | sort -u

  16. Matt:

    Thanks a lot — this is a very helpful no-nonsense guide!

  17. Albert:

    Hey - this might help for creating tags from branches:

    git branch -r | awk ‘{ print “git tag “$1″ tags/”$1 ” && git branch -r -d tags/”$1 }’

Leave a comment