Back in August 2019, Bitbucket announced they were going to stop supporting Mercurial repositories and they set the deadline of June 1st 2020 when all of the Mercurial repositories will be deleted. It is kind of sad, I really like Mercurial - it was the first distributed VCS I started using almost ten years ago, when Atlassian took the groundbreaking move to offer hosting of private repositories for free (GitHub started offering private repositories only in 2019).

So the decision had to be made: do I stay at Bitbucket and try to convert Hg repositories to git, or do I move them elsewhere? Back in August, Atlassian did not provide any kind of tool to make the conversion from Hg to git easy, so I decided to wait a few months, hoping this would change. Unfortunately, the wait was in vain. I'm not really sure why Atlassian chose this path that will make their users' lives difficult. I guess they took a calculated decision not to invest money into this because most of their Hg repositories were from non-paying customers (like me) or were no longer being used.

Since I already have some newer repositories at GitHub, and since GitHub provides a web user interface to import Mercurial repositories from elsewhere, this made my decision easier: move to GitHub and git.

First, the results

I managed to migrate all of my Bitbucket Hg repositories (around ten of them) in a day or two. Some of these repositories are small, some are big(ish) (the largest one containing around 130,000 objects and using 1.12 GB), some are private and some are public. Most of them are single-user repositories and I did not use more advanced Bitbucket features (except Issues). The process was straightforward, but there were some gotchas mostly due to the bad user interface of the GitHub's Import tool. What follows are the notes I took during the process and I hope you find them useful.

Obligatory disclaimer

I do not take responsibility for any kind of damage the information in this article may cause you. Source code repositories are one of the most important and valuable things developers posses, so tread carefully. I recommend trying the process on a smallest repository first, so you get the feel for the process.

Also, please read the whole article before doing any kind of migration as there are some important things you need to know about before moving your code.

Caveat: I can confirm that the process described here (using GitHub's Import tool) does not import Bitbucket Issues. About other Bitbucket features (like Pull requests, Pipelines, Deployments etc.) I cannot really say, since I didn't use them in my repositories, but I would take an educated guess and say "probably not", as these are features of the Bitbucket itself, not of the source code repository. There are some tools that claim they migrate Issues though (see the links at the bottom).

The good news is that the tool does migrate Hg branches to git (which should not be taken as an afterthought, since Hg branches and git branches are not the same thing).

If you're using MFA on Bitbucket

If you're using multi-factor authentication on Bitbucket (which you definitively should!) and you want to migrate private repositories, you will first need to disable MFA (on Bitbucket, not GitHub). GitHub's Import tool will not work with MFA enabled. It will not complain if MFA is enabled, but the import process will stall in an infinite loop of GitHub requesting your Bitbucket credentials over and over again.

Once you're finished with migrating all of your repositories, don't forget to re-enable MFA on Bitbucket. Unfortunately, you will have to re-register all of your security keys and authenticator apps from scratch.

How to start

To start migrating a repository from Bitbucket, go to the Import repository page on GitHub, and enter the requested information:

  • Your old repository’s clone URL: enter the HTTPS URL of the repository in Bitbucket (note that SSH URL is not supported by the Import tool).
  • Your new repository details: Owner will be auto-filled with your GitHub user, Name is the name of the new repository in GitHub, which will be created automatically.
  • !!! WARNING, WARNING, WARNING !!!: be sure to set the Privacy to Private if you're migrating a private repository. The default option is Public, which was a really bad user interface decision on GitHub's part. If you forget to set this, your private repository will be exposed to the world immediately after the importing finishes. And, as you know, once it's public, it's too late.

Initiating the import

After clicking on the Begin Import, the importing process will start. When importing private repositories, the tool will soon ask you for Bitbucket credentials. This is another potential source of confusion, since the credentials input form runs under the domain and if you have GitHub account credentials stored in your browser, it will try to fill in those (instead of Bitbucket ones). So be careful to enter the right ones, as the tool does not really indicate a problem if the credentials are incorrect.

While you're waiting

Depending on the size of the repository, the import process can run from a few minutes to several hours. You don't need to wait by your computer for the import to finish, since GitHub will send you an email once the repository has been imported (or if an error occurred during the importing).

Matching authors

During the importing process (or even after the whole repository has been imported), the Import tool offers you the ability to match users (committing accounts) from Bitbucket to GitHub ones, one by one. You can match them in two ways:

  1. By entering the GitHub username (this is the approach I used).
  2. By specifying GitHub user's email address: this one did not work for me since (as I understand it, the email address has to be public).

Initially, the Update commit authors dialog was another confusing part of the process for me because its user interface is not ideal, but once you get a hang of it, it shouldn't be a problem.

Converting .hgignore files

Assuming you have successfully imported the repository, one of the first things you will need to do is convert any .hgignore files to .gitignore. While the two file formats are not identical, in my case just renaming the file did the trick.

Testing the migration

Before jumping in joy and deleting any Bitbucket repositories, I recommend testing your migration. That means checking out the code and trying to build it from scratch. It is also a good opportunity to freshen up your build documentation with any missing steps.

Do it now

Since I'm writing this at the end of January and there are still four months until the deadline, it is tempting to postpone the migration to the last possible moment, especially since this is a kind of job that most developers don't really like.

I can give you two reasons why postponing is not a good idea:

  1. The migration process takes time and burns GitHub's hardware resources. Even now in January, when I expect there's not much migrating activity, larger repositories can take several hours to migrate. Once we approach the deadline and all the people start migrating, this will probably take even longer.
  2. If you migrate early, you will still have the time to repeat the migration and fix any issues (provided by don't delete the original Bitbucket repository).

Useful links

That is about it. If you have any additional suggestions or comments, please contact me on Twitter.