Migrating from SVN to Git

So you’ve done it – you’ve finally made the decision to switch to Git. SVN does some things very well, and has been a great source control system since it’s creation in 2000. But the features that Git brings – distribution, performance, easy branches, easy merges, stash – are hard to pass up. After you make the switch, you’ll probably wonder how you ever worked without it.

So how do you get all of your data, branches, tags and history into Git? Git includes an incredibly useful tool, git-svn which is a bidirectional connection between Git and SVN. It allows you to pull, and if you so desire push, commits to and from SVN. I recommend avoiding pushing back to SVN because, well, why would you? We’re here to switch, not combine! We’ll just use it to pull down all commit history, branches and tags from SVN.

The general workflow for this process is:

  • Initialize a Git repository with the SVN repository as a remote
  • Configure the user mapping between SVN and Git
  • Fetch from the SVN repo
  • Convert the SVN tags and branches into Git tags and branches
  • Push the repository to a bare repo on the Git host

To start, initialize a git repository with the svn repository as a remote:

> git svn init http://url.to.svn/ --prefix svn

The --prefix svn will prefix all branches and tags with the word svn which will make it easier to distinguish them later on. If you have a non-standard SVN layout (i.e. not named trunk, branches and tags), you can specify each of those with -T for Trunk, -B for Branches and -t for Tags:

> git svn init http://url.to.svn/ -T Trunk -B Branches -t Tags

Next, create an authors.txt file that maps the SVN usernames to the desired usernames in Git. The format is My Svn Username = My Name <myemail>. For instance:

davidzych = David Zych <dave@example.com>
johncandy = John Candy <john@example.com>
michaelscott = Michael Scott <michael@dundermifflin.com>

Once you have the authors file, configure Git-Svn to use the file when performing the fetch:

> git config svn.authorsfile ../authors.txt

You could also configure this in the global config if you have many repos to migrate and only want to specify it once.

It is at this point that I recommend switching the permissions on SVN to be readonly. This way, no one can commit to the repository while you’re performing the migration and no commits will be lost. Once you’ve done that, fetch from svn:

> git svn fetch

After what is probably going to be a long time, you’ll have a git repository with a lot of commits with funny looking commit messages and some remote branches that apparently are your SVN branches and you might be feeling pretty good right now. But you have more work to do! If you do a git branch -a, you’ll see all of your branches from SVN listed as remote branches:

> git branch -a
    * master
    remotes/svn/trunk
    remotes/svn/feature-123
    remotes/svn/feature-456

We need to take those remote branches and turn them into local Git branches. To do this you can run git branch branch-name remotes/svn/branch-name. If you only have a few, you can run that command manually for each branch and be done with it. If you have a lot, well, you can use Powershell or something similar to loop through the branches and automate it, or be like me and copy the branches into Excel, create a formula that generates the create branch statements and save those as a batch file and run it. I’m not an Excel fan but, hey, it works. However you want to do it, get it done.

After branches, you can do the same thing with tags:

> git tag -a -m "Migrating SVN tag" tag-name refs/tags/tag-name

Now you have all your branches and tags as local branches and tags in Git.

Next, add your bare remote Git repository as a remote (you did create one of those, right?).

> git remote add newrepo https://url.to.git/repo.git

And push everything up! Remember to specify --all to push all local Git branches, and perform a second push with --tags to push all tags.

> git push --all newrepo
> git push --tags newrepo

You now have all of your commits, branches and tags from SVN migrated to Git. Instead of attempting to clean out your Git-SVN hybrid repository, it’s probably easiest to perform a clean checkout of the new repository before you start working again:

> git clone https://url.to.git/repo.git

And, with that, you’re done! Enjoy your new Git repository!