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.
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
@echo off exit 0
Now you can run
## 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. ...
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.confand 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.
--datesortparameter preserves the revision order of the Subversion repository. Without this
hg convertwould convert one branch after another resulting in a different revision order.
- You may want to create a file called
.hgdirectory 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.
path/to/svnreposyou 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).
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
Two thing are different here from the command above:
- The path is specified by using
file://.... Specifying the path without this doesn’t work in this case.
- 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).