Skip to main content

Git Repository Migration Guide

When to Use

When you want to migrate an entire repository from one hosting platform to another while preserving branches, tags, and other refs, a mirror migration is usually the safest approach.

Common scenarios include:

  • Migrating from Gitee to GitHub
  • Migrating from a personal repository to an organization repository
  • Preserving full history and tags, not just the default branch

Check Current State First

  • The target repository should ideally be a newly created empty repository to avoid --mirror overwriting existing refs.
  • Confirm you have read/write access to both the source and target repositories.
  • If the repository uses Git LFS, large file objects need additional migration -- a simple git push --mirror won't handle them.

Full Migration of All Branches and Tags

git clone --mirror git@gitee.com:username/repo-name.git repo-name.git
cd repo-name.git
git remote set-url origin git@github.com:username/repo-name.git
git push --mirror

This set of commands migrates all refs, not just the current branch.

Common Scenarios

Basic Checks After Migration

git remote -v
git for-each-ref --format='%(refname:short)' refs/heads refs/tags
git ls-remote --heads --tags origin

Just Want to Point the Working Copy to a New Remote

If you're not doing a "full repository mirror migration" but just want to switch an existing working directory to a new remote:

git remote set-url origin https://github.com/username/repo-name.git
git remote -v

Repository Uses Git LFS

git push --mirror only syncs Git refs -- it won't automatically upload LFS large file objects. In this case, run the following in a regular working copy:

git clone git@gitee.com:username/repo-name.git repo-name
cd repo-name
git lfs fetch --all
git remote add github git@github.com:username/repo-name.git
git lfs push --all github

If you haven't configured Git LFS yet, see Cloning Large Files with Git LFS.

Optional Advanced Operation: Rewrite Commit Email Addresses

If your goal is to have old commits attributed to a new email in GitHub Contributions, use git filter-repo -- don't treat git filter-branch as the default choice.

Operate in a temporary mirror copy:

git filter-repo --commit-callback '
if commit.author_email == b"old-email@example.com":
commit.author_name = b"Your Name"
commit.author_email = b"github-email@example.com"
if commit.committer_email == b"old-email@example.com":
commit.committer_name = b"Your Name"
commit.committer_email = b"github-email@example.com"
'
git push --force --mirror

This rewrites history and is only appropriate when you fully understand the consequences and have coordinated with collaborators.

Risks and Boundaries

  • git push --mirror makes the target refs identical to the local mirror -- it will also delete refs that exist on the target but not locally.
  • If the target repository already has a README, initial commit, or default tags, the mirror push may overwrite them.
  • History-rewriting operations change commit hashes; if anyone has already developed based on the old history, you must coordinate in advance.
  • git filter-branch still works, but it's now considered a legacy tool and is not recommended as the default approach.