← Back to DevBytes

Migrating from Mercurial to Git

Understanding the Migration Landscape

Migrating from Mercurial (Hg) to Git involves transferring your entire version control history—branches, tags, commits, and authorship metadata—from a Mercurial repository into a Git repository. This process has become increasingly common as Git has emerged as the dominant version control system across the industry, supported by platforms like GitHub, GitLab, and Bitbucket with Git-first workflows.

Mercurial and Git share many conceptual similarities: both are distributed version control systems, both use directed acyclic graphs for commit history, and both support branching and tagging. However, their internal representations differ significantly. Mercurial stores changesets as immutable objects with revision numbers that are local to each repository, while Git uses content-addressable storage with globally unique SHA-1 hashes. Understanding these differences is crucial for a successful migration.

Why Migration Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Several factors drive teams to migrate from Mercurial to Git:

Prerequisites and Preparation

Before beginning the migration, ensure you have the following tools installed and accessible:

Verify your installations with these commands:

# Check Git version
git --version

# Check Mercurial version
hg --version

# Check Python 3 availability
python3 --version

Clone the fast-export tool repository, which contains the essential conversion utilities:

git clone https://github.com/frej/fast-export.git
cd fast-export

This tool suite includes hg-fast-export.sh, which handles the core conversion logic, mapping Mercurial changesets to Git commits while preserving author information, commit messages, and branch structures.

Step-by-Step Migration Process

Step 1: Create a Clean Mercurial Working Copy

Start with a fresh clone of your Mercurial repository to avoid any local state corruption:

hg clone https://your-hg-server/your-project mercurial-migration-temp
cd mercurial-migration-temp

Verify the integrity of your Mercurial repository before proceeding:

hg verify
hg summary

Take note of all branches, tags, and bookmarks present in the repository:

hg branches
hg tags
hg bookmarks

Step 2: Configure Author Mapping

Create an authors mapping file to translate Mercurial usernames to Git-style author identifiers. This file uses the format hg_username = Git Name <email@domain.com>:

# Create authors.txt in your working directory
cat > authors.txt << 'EOF'
john_doe = John Doe 
jane_smith = Jane Smith 
alex.wilson = Alex Wilson 
buildbot = Build System 
EOF

To extract all unique authors from your Mercurial history, use this command:

hg log --template '{author}\n' | sort | uniq

Feed this output into your authors.txt file to ensure no authorship data is lost during conversion.

Step 3: Initialize the Target Git Repository

Create a new directory for your Git repository and initialize it:

mkdir ../git-migration-target
cd ../git-migration-target
git init --initial-branch=main

The --initial-branch flag sets the default branch name to main (or any name matching your team's convention), avoiding the legacy master default.

Step 4: Run the Conversion

Navigate back to the Mercurial working directory and execute the fast-export conversion script, pointing it at your initialized Git repository:

cd ../mercurial-migration-temp

# Ensure the fast-export script is executable
chmod +x ../fast-export/hg-fast-export.sh

# Execute the conversion
../fast-export/hg-fast-export.sh --force -A ../authors.txt

Important flags explained:

The conversion process iterates through every Mercurial changeset, converting each to a Git commit object. For large repositories, this may take several minutes or hours depending on commit count and repository size.

Step 5: Verify the Converted Repository

After conversion completes, navigate to the Git repository and run comprehensive validation checks:

cd ../git-migration-target

# Check branch structure
git branch -a

# Verify tags transferred correctly
git tag -l

# Examine commit history
git log --oneline --graph --all --decorate

# Verify commit count matches
git rev-list --count --all

# Check file integrity
git fsck --full

Compare the commit count between your original Mercurial repository and the new Git repository:

# In Mercurial repository
hg log -r 'all()' --template '{rev}\n' | wc -l

# In Git repository
git rev-list --count --all

These numbers should match exactly, accounting for merge commits which may be counted slightly differently depending on your Mercurial branching topology.

Step 6: Clean Up and Optimize

Perform Git repository maintenance to optimize storage and remove unnecessary loose objects:

# Aggressively repack and prune
git repack -a -d -f --depth=250 --window=250
git prune-packed

# Remove unreachable objects
git reflog expire --expire=now --all
git gc --aggressive --prune=now

If your team uses Git LFS (Large File Storage), now is the time to migrate large binary files out of the repository history:

# Install Git LFS
git lfs install

# Track binary file patterns
git lfs track "*.png"
git lfs track "*.zip"
git lfs track "*.tar.gz"

# Add the .gitattributes file
git add .gitattributes
git commit -m "Configure Git LFS tracking"

Step 7: Configure Remote and Push

Add your target remote repository and push all branches and tags:

git remote add origin https://github.com/your-org/your-project.git

# Push all branches and tags
git push origin --all
git push origin --tags

If your repository contains many branches, consider pushing them in batches to avoid timeouts on large pushes:

# Push main branch first
git push origin main

# Then push remaining branches
for branch in $(git branch -r | grep -v 'origin/main'); do
    git push origin $branch
done

Handling Complex Migration Scenarios

Subrepositories (Hg Subrepos)

Mercurial subrepositories do not have a direct Git equivalent. Git submodules serve a similar purpose but with different semantics. During migration, you have several options:

For the submodule approach, first migrate each subrepo separately, then integrate:

# After migrating subrepo to Git
cd main-project
git submodule add https://github.com/your-org/subproject.git lib/subproject
git submodule init
git submodule update

git add .gitmodules lib/subproject
git commit -m "Integrate migrated subrepository as Git submodule"

Named Branches vs. Git Branches

Mercurial's named branches are permanent metadata on commits, while Git branches are lightweight movable pointers. The fast-export tool creates Git branches corresponding to each active Mercurial named branch. Closed Mercurial branches require special attention:

# Before conversion, list closed branches
hg branches -a

# The fast-export tool typically preserves these as Git branches
# Verify post-conversion with:
git branch --list

If you had bookmark-based workflows (similar to Git branches), the conversion preserves these as actual Git branches automatically.

Large Repositories and Performance

For repositories with tens of thousands of commits, the conversion process can be resource-intensive. Optimize the process with these techniques:

# Use a RAM disk for temporary storage (Linux/macOS)
mkdir -p /tmp/ramdisk
sudo mount -t tmpfs -o size=8G tmpfs /tmp/ramdisk
cd /tmp/ramdisk

# Perform the entire conversion on the RAM disk
hg clone /path/to/source hg-temp
git init git-target
cd hg-temp
/path/to/fast-export/hg-fast-export.sh --force -A /path/to/authors.txt

# Copy result back
cp -r ../git-target /path/to/final-destination

Alternatively, break the conversion into incremental chunks using Mercurial's revset capabilities:

# Convert in batches of 1000 commits
# This requires custom scripting and is advanced usage
hg log -r 'first(revs, 1000)' --template '{rev} {node}\n'

Post-Migration Verification Checklist

Before announcing the migration as complete, run through this verification checklist:

# 1. Commit count parity
echo "Hg commits: $(hg log -r 'all()' --template '{rev}\n' | wc -l)"
echo "Git commits: $(git rev-list --count --all)"

# 2. Branch comparison
echo "Hg branches: $(hg branches | wc -l)"
echo "Git branches: $(git branch -r | wc -l)"

# 3. Tag integrity
echo "Hg tags: $(hg tags | wc -l)"
echo "Git tags: $(git tag -l | wc -l)"

# 4. Random commit spot-check
# Pick 10 random revisions and verify messages match
hg log -r 'random(10)' --template '{node} {desc}\n' > hg_sample.txt
# Manually verify against git log output

# 5. File tree comparison
hg manifest --all > hg_files.txt
git ls-tree -r HEAD --name-only > git_files.txt
diff hg_files.txt git_files.txt

# 6. Final repository health check
git fsck --full --strict
git repack -a -d --depth=250 --window=250

Best Practices for a Smooth Migration

Plan a Cutover Window

Schedule the migration during a period of low development activity. Communicate the timeline clearly to all team members. A typical migration workflow includes:

Preserve the Original Repository

Never delete or overwrite the original Mercurial repository during migration. Keep it archived and read-only as a fallback reference:

# Archive the Mercurial repository
tar -czf hg-archive-$(date +%Y%m%d).tar.gz /path/to/original-hg-repo

# Store in long-term backup location
# Consider adding a README noting the migration date and new repository URL

Test CI/CD Pipelines First

Before announcing the migration to the entire team, configure and test your continuous integration pipelines against the new Git repository:

# Sample CI test script
git clone https://github.com/your-org/your-project.git ci-test
cd ci-test
# Run your build process
make build
make test
# Verify all tests pass identically to Mercurial-based runs

Educate the Team

Prepare training materials that map Mercurial commands to their Git equivalents:

# Common command mappings
# hg clone  -> git clone
# hg pull   -> git fetch
# hg push   -> git push
# hg commit -> git commit -a (staging is different!)
# hg log    -> git log
# hg diff   -> git diff
# hg status -> git status
# hg branch -> git branch
# hg merge  -> git merge
# hg update -> git checkout / git switch

Emphasize the key conceptual differences: Git's staging area (index), the distinction between git pull and git fetch, and the fact that Git branches are lightweight pointers rather than permanent commit metadata.

Handle Build Dependencies and Hooks

Mercurial hooks (defined in .hg/hgrc) do not transfer automatically. Reimplement them as Git hooks:

# Example: Translating an Hg pre-commit hook to Git
# Original .hg/hgrc:
# [hooks]
# pre-commit = python3 validate_code.py

# Git equivalent (.git/hooks/pre-commit):
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
python3 validate_code.py
EOF
chmod +x .git/hooks/pre-commit

For team-wide hooks, configure them in your repository's .githooks directory and instruct team members to set git config core.hooksPath .githooks.

Common Pitfalls and Troubleshooting

Encoding Issues

If your Mercurial repository contains non-UTF-8 commit messages (common in legacy projects), specify the encoding explicitly:

../fast-export/hg-fast-export.sh --force -A authors.txt -e ISO-8859-1

After conversion, verify encoding correctness:

git log --encoding=UTF-8 --show-encoding

Missing Tags

Sometimes Mercurial tags fail to transfer. Manually create them post-migration:

# List tags that failed to migrate
hg tags --quiet | sort > hg_tags.txt
git tag -l | sort > git_tags.txt
diff hg_tags.txt git_tags.txt

# For each missing tag, find the corresponding commit and create it
git tag -a v2.1.4  -m "Tag migrated from Mercurial"

Large File Bloat

If your repository becomes too large due to binary history, use BFG Repo-Cleaner or git filter-repo to excise large files:

# Using git filter-repo (recommended modern tool)
pip install git-filter-repo

# Identify large files
git filter-repo --analyze

# Strip files larger than 50MB
git filter-repo --strip-blobs-bigger-than 50M --force

Automating the Migration with a Script

For teams managing multiple Mercurial repositories, automate the entire process with a comprehensive migration script:

#!/bin/bash
# migrate-hg-to-git.sh - Complete Mercurial to Git migration script

set -euo pipefail

HG_REPO_URL="$1"
GIT_REMOTE_URL="$2"
AUTHORS_FILE="authors.txt"

echo "=== Cloning Mercurial repository ==="
hg clone "$HG_REPO_URL" hg-migration-temp
cd hg-migration-temp

echo "=== Extracting authors ==="
hg log --template '{author}\n' | sort | uniq > ../authors_raw.txt

echo "=== Please edit authors.txt to add email addresses ==="
echo "Format: hg_username = Full Name "
echo "Waiting 30 seconds for you to edit the file..."
sleep 30

echo "=== Initializing Git repository ==="
mkdir ../git-target
cd ../git-target
git init --initial-branch=main
cd ../hg-migration-temp

echo "=== Running fast-export conversion ==="
/path/to/fast-export/hg-fast-export.sh --force -A ../authors.txt

echo "=== Verifying conversion ==="
cd ../git-target
HG_COMMITS=$(cd ../hg-migration-temp && hg log -r 'all()' --template '{rev}\n' | wc -l)
GIT_COMMITS=$(git rev-list --count --all)
echo "Hg commits: $HG_COMMITS | Git commits: $GIT_COMMITS"

echo "=== Optimizing repository ==="
git repack -a -d -f --depth=250 --window=250
git gc --aggressive --prune=now

echo "=== Adding remote and pushing ==="
git remote add origin "$GIT_REMOTE_URL"
git push origin --all
git push origin --tags

echo "=== Migration complete ==="
echo "Repository available at: $GIT_REMOTE_URL"

Conclusion

Migrating from Mercurial to Git is a well-understood process with mature tooling that preserves your complete version control history. The fast-export tool chain handles the heavy lifting of commit conversion, branch mapping, and author translation, while the verification steps ensure data integrity throughout the transition. By following the structured approach outlined in this tutorial—preparing author mappings, running the conversion, verifying results, and implementing post-migration optimizations—you can move your project to Git with confidence. The investment in migration pays dividends through access to Git's vast ecosystem of tools, hosting platforms, and collaborative workflows that have become the standard for modern software development. Remember to archive your original Mercurial repository, train your team on Git's conceptual model, and iterate on your CI/CD configurations to fully realize the benefits of your new version control environment.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles