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 standardtrunk/,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 addinggit-svn-idmetadata 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:
-
Install
hg-fast-export:pip install hg-fast-export(You might need
pip3depending on your Python installation.) -
Install
git-filter-repo(recommended for cleaning up history):pip install git-filter-repo
The Process:
-
Clone your Mercurial repository:
hg clone <mercurial_repo_url> <local_hg_repo_dir> cd <local_hg_repo_dir> -
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 -
Create a new, empty Git repository:
mkdir ../my-git-repo cd ../my-git-repo git init -
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 svnandhg-fast-exporthave 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, andgit-svn-id. Mercurial has similar metadata. Git has author, committer, date, and the commit hash. Tools like--no-metadataforgit svnandhg-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.