Git is fundamentally different from SVN and Mercurial, and understanding these differences is key to a smooth migration.

Let’s look at how to migrate from SVN and Mercurial to Git, preserving your history.

Migrating from SVN to Git

The git svn command is your primary tool here. It’s a powerful Git subcommand that acts as a bridge to Subversion repositories. The basic idea is to "clone" your SVN repository into a Git repository, translating SVN’s centralized model into Git’s distributed one.

The Core Command:

git svn clone <svn_repo_url> --stdlayout --no-metadata -A authors.txt <local_git_repo_dir>
  • <svn_repo_url>: The URL of your Subversion repository (e.g., http://svn.example.com/myproject).
  • --stdlayout: This is crucial if your SVN repo follows the standard trunk/, branches/, tags/ layout. If it doesn’t, you’ll need to specify the locations of trunk, branches, and tags manually using --trunk, --branches, and --tags.
  • --no-metadata: This prevents Git from adding git-svn-id metadata to commits, which keeps the history cleaner and more "native" Git.
  • -A authors.txt: This maps SVN commit author names to Git author names and emails. Without it, Git will use the SVN usernames, which are often not real names or email addresses.
  • <local_git_repo_dir>: The name of the directory where your new Git repository will be created.

authors.txt Example:

user.name = Real Name <real.name@example.com>
another_user = Another Person <another.person@example.com>

Initial Clone (This can take a while for large repos):

git svn clone http://svn.example.com/myproject --stdlayout --no-metadata -A authors.txt myproject-git
cd myproject-git

Handling Branches and Tags:

git svn clone by default only imports the trunk as the master branch. Branches and tags are imported as remote-tracking branches under refs/remotes/origin/. You’ll need to convert these into proper Git branches and tags.

Converting Remote Branches to Local Branches:

git for-each-ref refs/remotes/origin | grep -v master | sed 's@^.*refs/remotes/origin/@@' | while read branch; do git branch "$branch" "origin/$branch"; done

This script iterates through all remote-tracking branches (except master), and for each one, creates a local branch with the same name pointing to the same commit.

Converting Remote Tags to Local Tags:

git for-each-ref refs/remotes/origin/tags | sed 's@^.*refs/remotes/origin/tags/@@' | while read tag; do git tag "$tag" "origin/tags/$tag"; done

This script does the same for tags.

Cleaning Up:

After converting, you’ll have local branches and tags. You can then delete the remote-tracking branches if you wish.

git branch -d -r origin/* # Delete remote-tracking branches
git gc --aggressive --prune=now # Clean up the Git repository

Pushing to a New Remote Repository:

Once you’re satisfied, you can add a new remote (like GitHub, GitLab, etc.) and push.

git remote add origin git@github.com:your-org/your-repo.git
git push -u --all origin
git push --tags origin

Migrating from Mercurial to Git

Mercurial has a dedicated tool for this: hg-fast-export. It’s a Python script that reads your Mercurial repository and outputs Git "fast-export" stream data, which Git can then import.

Prerequisites:

  1. Install hg-fast-export:

    pip install hg-fast-export
    

    (You might need pip3 depending on your Python installation.)

  2. Install git-filter-repo (recommended for cleaning up history):

    pip install git-filter-repo
    

The Process:

  1. Clone your Mercurial repository:

    hg clone <mercurial_repo_url> <local_hg_repo_dir>
    cd <local_hg_repo_dir>
    
  2. Generate the Git fast-export stream: This command dumps the entire Mercurial history into a file named git-history.dump.

    hg fast-export --all > git-history.dump
    
  3. Create a new, empty Git repository:

    mkdir ../my-git-repo
    cd ../my-git-repo
    git init
    
  4. Import the fast-export stream: This reads the dump file and populates the new Git repository.

    git fast-import < ../my-hg-repo/git-history.dump
    

Handling Authors:

Similar to SVN, Mercurial author names might not be in the Name <email> format. hg-fast-export can use a mapping file.

authors.txt Example (same format as SVN):

user.name = Real Name <real.name@example.com>
another_user = Another Person <another.person@example.com>

Re-running with Author Mapping:

If you need to use an author map, you’ll regenerate the dump:

cd ../my-hg-repo
hg fast-export --all --usermap ../authors.txt > git-history.dump
cd ../my-git-repo
git fast-import < ../my-hg-repo/git-history.dump

Cleaning Up and Rewriting History (Optional but Recommended):

The git fast-import command creates commits as-is. If you want to clean up author names, emails, or commit messages, you can use git filter-repo.

First, make sure you have a clean branch to work on (e.g., main or master). You might need to create it if fast-import didn’t name it.

# If you don't have a main/master branch, create one from the latest commit
git checkout -b main HEAD

Then, run git filter-repo. For example, to normalize author names and emails:

git filter-repo --mailmap ../authors.txt

Pushing to a New Remote Repository:

git remote add origin git@github.com:your-org/your-repo.git
git push -u --all origin
git push --tags origin

Key Differences and Why They Matter

  • Centralized vs. Distributed: SVN is strictly centralized; all history lives on the server. Mercurial is distributed but often used in a centralized workflow. Git is inherently distributed. This means git svn and hg-fast-export have to perform a conceptual translation.
  • Revision Numbers vs. Hashes: SVN uses sequential revision numbers. Mercurial also uses sequential revision numbers per repository but can have complex internal IDs. Git uses SHA-1 hashes, which are unique and immutable identifiers for every commit. The migration process maps SVN/Mercurial revisions to Git commits.
  • Branching and Tagging: SVN branches and tags are just copies in a special directory. Mercurial branches and tags are more integrated but still distinct concepts. Git branches are lightweight pointers to commits, and tags are also pointers. The migration tools create Git branches and tags that accurately reflect the SVN/Mercurial history.
  • Metadata: SVN commits have svn:log, svn:author, svn:date, and git-svn-id. Mercurial has similar metadata. Git has author, committer, date, and the commit hash. Tools like --no-metadata for git svn and hg-fast-export’s handling aim to produce Git history that looks like it was always Git.

The next challenge is often managing large binary files that were stored in SVN or Mercurial, which Git isn’t ideal for.

Want structured learning?

Take the full Git course →