By admin , 29 April 2026

Why migrate

Mercurial and Git are sibling systems - both distributed, both content-addressed, both supporting branches and merges. Migrating is mostly a question of tooling: most hosting providers, CI services, and IDEs now invest more in Git than in Mercurial. For teams choosing tooling alignment, the move is straightforward.

The standard tool: fast-export

The hg-fast-export tool emits Mercurial history in Git's fast-import stream format. It preserves authors, dates, branches, tags, and merges.

# Clone hg-fast-export
git clone https://github.com/frej/fast-export.git
cd ~/path-to-hg-repo
git init ../git-repo
cd ../git-repo
~/fast-export/hg-fast-export.sh -r ../path-to-hg-repo
git checkout HEAD

Authors mapping

Mercurial commit authors are free-form strings; Git expects "Name <email>". Build a mapping:

# authors.txt
"Jane Doe" = "Jane Doe <[email protected]>"
"bob" = "Bob Smith <[email protected]>"
~/fast-export/hg-fast-export.sh -r ../hg-repo -A authors.txt

Branch handling

Mercurial named branches are not the same as Git branches. By default, hg-fast-export maps named branches to Git branches, but the semantics differ:

  • Mercurial named branches are permanent labels on commits.
  • Git branches are mutable pointers.

If your team uses Mercurial bookmarks, those map more cleanly. Pass -B to migrate bookmarks as Git branches.

Closed branches

Mercurial allows "closing" branches without deletion. By default, closed branches become Git branches; you may want to delete them after migration:

git for-each-ref refs/heads/closed-* --format='%(refname:short)' \
  | xargs -n 1 git branch -D

Tags

Mercurial tags are themselves commits to a .hgtags file. fast-export converts these to Git tags automatically. Verify:

hg tags
git tag

Counts should match (minus Mercurial's "tip" pseudo-tag).

Verifying integrity

hg log --template '{node}\n' | wc -l
git log --all --pretty=format:'%H' | wc -l
# Counts should match

Spot-check by comparing file content at known commits:

hg cat -r <hg-rev> path/to/file > /tmp/hg-file
git show <git-sha>:path/to/file > /tmp/git-file
diff /tmp/hg-file /tmp/git-file

Pushing to a Git host

git remote add origin https://github.com/team/repo.git
git push -u origin --all
git push origin --tags

Workflow translation

Mercurial users are often shocked by Git's index. Bookmarks behave like branches; named branches do not. hg pull equals git fetch, not git pull. hg commit commits everything tracked; git commit commits the index. A short workshop bridges the gap.

Extensions and hooks

Mercurial's extension system has no direct Git counterpart. mq (Mercurial Queues) → git rebase -i. histedit → also git rebase -i. Mercurial hooks rewrite as Git hooks; consult .hg/hgrc for what to migrate.

Aftercare

Keep the Mercurial repo read-only for at least a release cycle. Document the SHA mapping in case anyone needs to find an old changeset.