#git | #hg

August 26, 2019

From Mercurial to Git

Update: It’s official, Mercurial is dead. The last hg stronghold has fallen:

We’ve decided to remove Mercurial support from Bitbucket Cloud and the API. Mercurial usage on Bitbucket is steadily declining, and the percentage of new Bitbucket users choosing Mercurial has fallen to less than 1%.

– Atlassian

Happy aliasing!


Original post from 2018-07-31

I recently went through some Mercurial to Git migrations. While stack overflow pretty much always has something to tell you, it never goes so smoothly in real life. Here’s my step-by-step guide including some random errors that you are bound to encounter.

Step 1 - fast-export

Get fast-export from https://github.com/frej/fast-export

Step 2 - upgrade Mercurial

Use pipenv for example to use the latest via clean install:

pipenv --two
pipenv shell
pipenv install mercurial
pipenv shell

Step 3 - encounter errors

Weird things may happen. Specially if you have a lot of history in the mercurial repository. Fear not, just strip a lot.

I encoutered the following:

master: Exporting simple delta revision 609/1718 with 0/1/0 added/changed/removed files
master: Exporting simple delta revision 610/1718 with 0/0/114 added/changed/removed files
master: Exporting simple delta revision 611/1718 with 15/0/0 added/changed/removed files
Traceback (most recent call last):
  File "../../git/fast-export/hg-fast-export.py", line 550, in <module>
    notes=options.notes,encoding=encoding,fn_encoding=fn_encoding,filter_contents=filter_contents))
  File "../../git/fast-export/hg-fast-export.py", line 441, in hg2git
    sob,brmap,hgtags,encoding,fn_encoding,filter_contents)
  File "../../git/fast-export/hg-fast-export.py", line 262, in export_commit
    export_file_contents(ctx,man,added,hgtags,fn_encoding,filter_contents)
  File "../../git/fast-export/hg-fast-export.py", line 139, in export_file_contents
    d=file_ctx.data()
  File "/Users/margus/src/venvs/api-1ofvI4PQ/lib/python2.7/site-packages/mercurial/context.py", line 1088, in data
    return self._filelog.read(self._filenode)
  File "/Users/margus/src/venvs/api-1ofvI4PQ/lib/python2.7/site-packages/mercurial/util.py", line 1437, in __get__
    result = self.func(obj)
  File "/Users/margus/src/venvs/api-1ofvI4PQ/lib/python2.7/site-packages/mercurial/context.py", line 702, in _filenode
    return self._filelog.lookup(self._fileid)
  File "/Users/margus/src/venvs/api-1ofvI4PQ/lib/python2.7/site-packages/mercurial/filelog.py", line 54, in lookup
    return self._revlog.lookup(node)
  File "/Users/margus/src/venvs/api-1ofvI4PQ/lib/python2.7/site-packages/mercurial/revlog.py", line 1501, in lookup
    raise LookupError(id, self.indexfile, _('no match found'))
mercurial.error.LookupError: data/handlers/__init__.pyc.i@6f9b45217cdc: no match found

No matter how much I tried to delete all possible references to the file, this happened again and again. So I decided to strip history from around there instead.

Step 4 - strip history

Enable convert extension in your hgrc

[extensions]
convert=

Pick your starting rev and shoot:

hg convert --config convert.hg.startrev=889 old-repo stripped-repo

Step 5 - fast-export, again

Hopefully you picked a good place, so the following goes without errors:

git init new-git-repo
cd new-git-repo
../fast-export/hg-fast-export.sh -r ../stripped-repo --force
git checkout HEAD
git remote add origin git@<your-upstream-url>
git push -u origin master

Pick your branches and push them as needed:

git branch --list
git push -u origin <branch-name>

Step 6 - cry a little

Mercurial is nice. But it has lost the race. Big platforms don’t support it, self hosting is pain and OSS has pretty much adapted Git as a standard. Have fun with configuring the hell out of you aliases now and reading through countless posts on how to actually use the beast. Like this three part guide which is missing the third part.. Well, resolving conflicts is hard.

Powered by Hugo & Kiss.