manski's blog

Migrating from Subversion to Mercurial

I’ve been working for quite some time now with Subversion but recently fell in love with Mercurial. Mercurial (like GIT or Bazaar) is a distributed version control system (DVCS). Coming from Subversion, it’s sometimes necessary to convert an existing Subversion repository to Mercurial. And that’s what this post is about.

Preparation

For this tutorial to work, you need a Subversion client and a Mercurial client. Unfortunately, the functionality required for this tutorial hasn’t been implemented in the popular graphical clients yet (such as TortoiseSVN), so we’ll need to stick with the command line.

  • Subversion: Windows users can download SlikSVN. Linux users can use their packaging system.
  • Mercurial: TortoiseHg comes with a command line client; otherwise go to mercurial.selenic.com and download the client there.

Note: The Subversion version doesn’t really matter here.

Creating a local copy

Although it’s possible to convert a Subversion repository directly over the internet/network, the whole process may not succeed on the first attempt. So it’s recommended to first create a local copy of the Subversion repository.

To achieve this, we’ll use one of Subversion’s command line programms: svnsync. First, we need to create an empty Subversion repository. Execute the following commands:

## Linux/Mac
$ cd /where/to/store/the/copy
$ svnadmin create .
## Windows
> cd where\to\store\the\copy
> svnadmin create .

Then you need to create a file in the directory hooks (which was created by svnadmin create) with the following contents.

On Linux/Mac (name it pre-revprop-change and make it executable via chmod +x pre-revprop-change):

#!/bin/sh
exit 0

On Windows (name it pre-revprop-change.cmd):

@echo off
exit 0

Now you can run svnsync.

## Linux/Mac
$ svnsync init file://`pwd` http://www.example.com/source/repository
Copied properties for revision 0.

$ svnsync sync file://`pwd`
Transmitting file data .
Committed revision 1.
Copied properties for revision 1.
Transmitting file data ..
Committed revision 2.
Copied properties for revision 2.
...
## Windows
# The quotes around %cd% are required for directory names containing spaces
> svnsync init file:///"%cd%" http://www.example.com/source/repository
Copied properties for revision 0.

> svnsync sync file:///"%cd%"
Transmitting file data .
Committed revision 1.
Copied properties for revision 1.
Transmitting file data ..
Committed revision 2.
Copied properties for revision 2.
...

Troubleshooting:

  • svnsync: Destination repository has not been initialized
    This error propably is the result of specifying an invalid URL for the source repository.

  • Transmitting file data .svnsync: no such table: rep_cache
    To solve this you need to edit the file db/fsfs.conf and add the line enable-rep-sharing = false.

  • svnsync: E165002: Storage of non-regular property svn:wc:ra_dav:version-url is disallowed through the repository interface, and could indicate a bug in your client.
    Are you trying to convert a repository from CodePlex? They don’t have actual Subversion repositories but Team Foundation repositories that can be accessed by Subversion via SvnBridge. Unfortunately, SvnBridge doesn’t support svnsync (yet). You can still convert such a repository to a Mercurial repository, but must specify the Internet source URL (instead of a local one).

Converting the repository

Mercurial come with an extension called “convert” that does just that: converting a non-Mercurial repository into a Mercurial repository.

First, the new Mercurial repository must be created (Windows commands work the same way):

$ hg init /path/to/new/repos

After that, converting the repository is as simple as that:

$ hg convert --datesort path/to/svnrepos path/to/hgrepos

And that’s it.

Notes:

  • The --datesort parameter preserves the revision order of the Subversion repository. Without this hg convert would convert one branch after another resulting in a different revision order.
  • You may want to create a file called authormap in the .hg directory of the new repository and a mapping from Subversion author names to real names.
  • SVN externals won’t be converted. You need to convert the external repositories yourself and/or use Mercurial subrepositories for them.
  • Instead of path/to/svnrepos you can also specify the URL to a remote SVN repository. This way you don’t need to sync the repository first to your local hard drive.

Converting sub project directory

Some repositories contain multiple projects with a directory structure like this:

+ proj1
+--- trunk
+--- tags
+--- branches
+ proj2
+--- trunk
+--- tags
+--- branches

The process to convert only one of those projects (=directories) differs a little bit from the process above.

First, you still need to sync the whole SVN repository. Although svnsync allows you to sync only certain subdirectories, this process will most likely introduce empty revision (i.e. when a revision only changed files in a directory that isn’t synced). If such an empty revision happens to be the head revision, the Mercurial conversion process will fail with svn: revision XXX not found (see issue 2834).

Next, in hg convert you need to use the file:// URL syntax for the SVN repository. So, the first parameter of the following command changes to this:

# On Linux
$ hg convert --datesort "file://`pwd`/path/to/svnrepos/proj2" path/to/hgrepos

Note that we’ve appended the subdirectory proj2 to the SVN path. This will only convert this subdirectory.

On Windows we need to do the same thing but we also need to convert the \ in the path to / before we can use it:

REM On Windows
> hg convert --datesort "file:///%cd:\=/%/path/to/svnrepos/proj2" path/to/hgrepos

Converting only a single directory

You can also convert it single directory from within the repository. To do this, do the same steps up to the convert command. Then use this command:

$ hg convert --datesort --config 'convert.hg.usebranchnames=False' file://`ls -d $PWD/path/to/svnrepos`/trunk/sources path/to/hgrepos

This just converts the directory trunk/sources from within the Subversion repository (which resides in the current directory under path/to/svnrepos).

Two thing are different here from the command above:

  1. The path is specified by using file://.... Specifying the path without this doesn’t work in this case.
  2. We specify the option convert.hg.usebranchnames. Without this option, the new Mercurial repository would contain only one branch named “sources” (in this example). With this option, the branch will be named “default” (which is the default).

One comment

  1. Pingback: Moving a Subversion/SVN including history to Mercurial/HG using TortoiseHG or HG command-line tools « The Wiert Corner – irregular stream of stuff

Leave a Reply

Your email address will not be published. Required fields are marked *