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

svnuser2 = Another User 

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 <git 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!

9 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 :)

Leave a comment