Tuesday, October 18, 2011

Migrating a Subversion Project to Git on BitBucket

Subversion is still the gold standard for version control (a.k.a. source control) systems, but the new kid on the block - Git - has undeniable advantages for many development models. Even for solo or centralised hierarchical development organisations, Git's agile branching and merging make it a joy to use.

But if you're going to go to the effort of switching version control systems, you probably also want to consider moving your repository to the cloud. This increases your options for off-site developers, and reduces the risks and maintenance costs of internally managed version control servers.

This article describes how to migrate a Subversion repository to Bitbucket, one of several online repositories.
First of all, why Bitbucket? GitHub and Google Code are worthy alternatives, but both have restrictions that make them less desirable for private repositories. Google Code doesn't allow them at all, and GitHub currently charges a small but significant amount per private repository. Bitbucket charges by developer instead, with the first five being free. Another important consideration is that all three are backed by large commercial companies that are unlikely to blink out of existence overnight.

1. Preparation
OK, enough of the justifications, lets get migrating! In the Subversion world, the convention is that you structure your projects as follows:
   - Trunk
   - Branches
      - Europe
      - US
   - Tags
      - Version 3.11
      - Version 3.12
If you use different conventions (like "trunk" instead of "Trunk"), take extra care to translate these in the code below. Our example Subversion project URL is svn://myserver/myrepo/foobar so you will need to translate this too.

Next, we need to fix a small discontinuity in user handling between Subversion and Git. Subversion users are simple usernames (like gregf), whereas Git requires a user's name and their email address. Create a file called authors.txt containing an entry for each of your subversion users (usually listed in svn/myrepo/conf/passwd):
lisas = Lisa Simpson <lisa@duff.com>
barts = Bart Simpson <bart@duff.com>
Finally. make sure you have git and git-svn installed. Google to find out how to do this on your operating system.

2. Convert the Subversion Project to a Local Git Repository
At the command line, enter:
git svn clone svn://myserver/myrepo/foobar --no-metadata -A authors.txt -b Branches -T Trunk -t Tags foobar_local
Make yourself some tea, coffee or martini - this will take a wee while, as it works through the entire history of changes in the Subversion project. Assuming all goes well, you should end up with a directory called foobar_local containing both your project, and more importantly a hidden directory called .git. To check, change into the directory and view the log:
cd foobar_local
git log
git branch
That last command doesn't show your branches! Don't panic, this will come right later.

3. Convert Ignore Filemasks
If you have set up filemasks for Subversion to ignore, we can easily convert these into the Git equivalent:
cd foobar_local
git svn show-ignore -i trunk > .gitignore
git add .gitignore
git commit -m 'Convert svn:ignore properties to .gitignore.'
Note that Trunk in Subversion has been lower-cased by Git to trunk.

4. Create a Bare Git Repository
So far you have a local git repository, but you need a bare repository to clone from. As part of this process, we set Git's symbolic HEAD to what was Subversion's (lower-cased) trunk, tell Git about any branches, and rename trunk to master (the convention in Git).
cd ..
git init --bare foobar_bare
cd foobar_bare
git symbolic-ref HEAD refs/heads/trunk
cd ../foobar_local
git remote add bare ../foobar_bare
git config remote.bare.push 'refs/remotes/*:refs/heads/*'
git push bare
cd ../foobar_bare
git branch -m trunk master
After the push, Git will list the new branches. I told you not to panic!

5. Convert Tags
Actually, if you use tags you might still be worried that all your tags seem to be branches. Git handles tags very differently from Subversion - in Git tags are simply aliases to a particular commit, whilst Subversion treats tags as a dead-end branch. Git-svn simply renames these branches, so we still need to convert these branch-tags to real Git tags:
cd foobar_bare
git for-each-ref --format='%(refname)' refs/heads/tags |
cut -d / -f 4 |
while read ref
   git tag "$ref" "refs/heads/tags/$ref";
   git branch -D "tags/$ref";

6. Push the Bare Repository to Bitbucket
Don't worry, we're nearly there now. The last step is to push all the branches up to Bitbucket. Log in to Bitbucket, and create a new repository called foobar. It will then show you the command required to clone this repository, which will include the HTTPS URL. The command will be something like this:

$ git clone https://myname@bitbucket.org/myname/foobar.git

The URL is the yellow section above. Now on your local machine, set the orgin and push the master (old trunk) and each branch to Bitbucket:
git remote add origin https://myname@bitbucket.org/myname/foobar.git
git push origin master
git push origin europe
git push origin us
Your project is now on Bitbucket.

7. Tidy Up
The only thing left now is to tidy up the local and bare repositories left on your machine.
cd ..
rm -rf foobar_local
rm -rf foobar_bare
And... you're done. Time for another tea, coffee, or martini to celebrate your success!

If this article has helped you, you can show your appreciation by telling any schools you are involved with about www.SchoolConferences.com. This simple online app has already revolutionised parent-teacher conference evenings for over a thousand schools. See how easy it is to book conferences with the demonstration event code HIGH4 for high schools, and ELEM4 for elementary schools. Thanks!


  1. Nice! I was actually looking for the last part only (pushing existing local git repository to Bitbucket) but it still worked! Thank you! (found via your Groups post)

  2. Great tutorial! I had to migrate some of our repositories over to BitBucket and this worked without a glitch.
    Thank you so much!

  3. You're a life-saver. I tried several how-tos... all of which lost me. With yours, I succeeded first time. Many thanks... and have a great Christmas.

  4. You really don't need step 4 (Create a Bare Git Repository). You should be able to push directly from you local repo to bitbucket

  5. Hi, thanks for the great tutorial! I ended up piecing a migration script together which automates the whole process. If anybody else is interested, here's the script:


  6. Don't forget to credit John Albin for showing you how to do the steps for SVN->Git migration


    1. And offcource Ludovic Bellière, for showing you how to push to BitBucket