August 31, 2015

Migrating a Bitbucket Mercurial project to Git

I am a long-time Mercurial user. It was my first choice after the switch from Subversion to the DVCS world. The decision I took by that time was based on how simple Mercurial is. From the design of the filesystem, the friendly and simple command line options, to the fact that with a single instalation, I was able to have hg web serving the central repositories to the team.

The time passed and I got used to Mercurial a lot. But looking around, seems that this is not the case for everyone else. Given the popularity of Github as a coding social network, the majority of services and tools has Git/Github support, if not both. Furthermore, Git has matured a lot, is very performant, and if the entire Linux Kernel uses it, why should I use anything else?

After resisting a lot for the change, I finally settle that it was time to finally make it. Now I am happy with the switch, and slowling I am moving my projects, specially the Open Source ones, from Hg to Git. After using Git for a while, the brain muscle memory toke care of the confusing CLI options and switches. I still land on StackOverflow to find some switches, tought.

Despite my change from Mercurial to Git, I'm probably going to stick with Bitbucket for some projects, specially the private ones and the ones at my corporation. The current pricing model of Bitbucket that charges for team members as opposed to repositories fits better in my monthly budget.

In this post, we will discuss a few ways to move from Mercurial to Git on Bitbucket, migrating the whole project, not only the repository.

Rename the old repo and create a new one

The first step is to backup your Mercurial repository, by renaming it. It is wise to also remove any WRITE permissions to avoid people trying to push to the wrong repository during the migration if it is a shared one.

To rename your repository go the project Settings page, then change the "Name" field to something different. In our case, I'll add the .hg suffix, so we can keep the same name for the new Git repository:


Once renamed, you can then reuse the old name to create a new Git project:

Convert from Mercurial to Git

Now that the project has was renamed, let's convert the project commit history into a new, Git project. The best way to accomplish that is using the hg-git extension. This extension makes a great deal to interact with Git repositories from Mercurial itself, allowing you to simply push to the Git new path from a local copy.

To install the extension, follow the instructions from their website. If you are using Linux and have easy_install on your machine, it should be as simple as:

$ easy_install --user hg-git

The --user flag tells easy_install to keep things on your home folder, making it easier to remove the extension later if you want to.

After you have the extension installed, enable it in your ~/.hgrc file:

[extensions]
...
hggit=

Now, it is time to push the changes to the new repository. In order to avoid losing any commits, let's push from a clean, bare Mercurial repo. The process is as simple as cloning, creating a bookmark named master for the default branch, then pushing to the git repository:

$ hg clone ssh://hg@bitbucket.org/username/repository.hg && cd repository.hg
$ hg bookmark -r default master
$ hg push git+ssh://git@bitbucket.org/username/repository.git

The .hg suffix on the first command is required only if you renamed the repository that way. On the other hand, the .git suffix on the Git repository name is mandatory so hg-git can make the push. The commands bellow assume that you have added your SSH key to the Bitbucket account.

Note: we need to create a bookmark to the default version called master. This allows the hg-git extension to also create the Git master branch. If you don't do that step, you will end up with a repository that display no branches or commits on the Bitbucket site.

The push URL git+ssh will convert the project using the hg-git extension first, then upload the contents to the Git repository specified. All changesets will be converted. It is important to notice that they will also all be recreated, generating new hashes based on the Git checksum.

Migrate Wiki

Bitbucket wikis are also repository tied to your project. However, the project wiki, once enabled, already has an initial commit. Because of that, there is no easy way to migrate the wiki history here, since hg-git will refuse to push to the new wiki repo since that wiki has a different ancestor. The easier way to migrate the wiki is to clone both the old wiki and the new one, and then copy the contents from the old folder to the new folder. Then you can push the changes to the new project wiki.

$ hg clone ssh://hg@bitbucket.org/username/repository.hg/wiki repository.hg-wiki
$ git clone git@bitbucket.org:username/repository.git/wiki repository.git-wiki
$ copy -r repository.hg-wiki repository.git-wiki
$ rm -r repository.git-wiki/.hg
$ cd repository.git-wiki && git push

Migrate Issues

If you also use the issue tracker, Bitbucket offers a nice export/import wizard to move them to the new repository. In the source project Settings page, go to Import & Export in the Issues menu. Then you can export the issues to a zip file, that you can later import to the new repository.

Note here that changeset references in the issue tracker will all be broken: since the repository was converted, the changesets references inside issue comments now point to a broken link. One way to fix this is to parse the issue file format, that is a JSON array of issue objects, and rewrite the changesets within it. In my case, I didn't bother having the dead links for now, but I'll update this post if I ever find a good way to keep the references later.

Conclusion

With the three steps bellow, you can convert the repository from Mercurial to Git, keeping the majority of the data on the new repository. The caveats are that some issue comment links, as well as the changeset ids, will be different on the new repository.

Happy Hacking!