Migrating a Branch Between Git Repositories and Preserving History

6 minute read

Goal: Change Repositories But Keep Git History

Recently, I found myself working on a user guide and some example code for a project. As the guide neared completion, we decided that it made more sense to include it as part of another repository. Moving it to the other repo would also simplify the process of adding it to the CI build process. The guide was in a long-lived branch that several people had contributed to over a period of severals months. I preferred to retain the commit history so that future contributors could see how it had evolved over time rather than simply moving the files and adding them to the new repository as a single commit.

Step 1: Prepare a Clean Copy of the Source Repository

This step involves deleting and re-cloning the source repository. Ensure that any and all changes you wish to retain have been pushed to a remote prior to proceeding.

Remove the existing local copy of the source repo:

rm -rf ${SOURCE_REPO}  # e.g. rm -rf ./source-repo

Next, clone the branch to be moved and change to that directory:

git clone --branch ${SOURCE_REPO_BRANCH} --origin ${SOURCE_REMOTE} --progress -v ${GIT_REMOTE_URL}/${SOURCE_REPO}.git  # e.g. git clone --branch feature-branch --origin origin --progress -v [email protected]:organization/source-repo.git
cd ${SOURCE_REPO}  # e.g. cd ./source-repo

Delete the remote to eliminate any possibility of accidentally pushing to it:

git remote rm ${SOURCE_REMOTE}  # e.g. git remote rm origin

Step 2: Filter Contents of the Source Repository

Git-filer-repo is a tool for re-writing Git history. It is a single-file python script, so all you have to do to use it is download the file and copy it into your $PATH.

With our source repository cloned, use git filter-repo to keep only the specific directory:

git filter-repo --path ${PATH_TO_DIRECTORY_TO_KEEP}  # e.g. git filter-repo --path ./path-to-keep

This command will extract the history of the specified directory. Any commits that only touched paths outside this directory will be removed.

Lastly, add the changes we made using git filter-repo and commit them:

git add .
git commit -m "Filtering history"

Step 3: Merge History Into the Destination Repository

Again, before proceeding, it would be prudent to ensure that any and all local changes have been pushed to a remote. Change directory to the destination repository location:

cd /path/to/${DESTINATION_REPO}  # e.g. cd /path/to/destination-repo

Add the source repo as a remote in the destination repo:

git remote add ${SOURCE_REPO} /path/to/${SOURCE_REPO}  # e.g. git remote add source-repo /path/to/source-repo

Ensure you are on the branch into which you will merge the history from the source repo. Pull from the source repo into the destination repo:

git branch  # check to make sure you are on the right branch
git checkout ${DESTINATION_BRANCH}  # if needed; e.g. git checkout destination-branch
git pull ${SOURCE_REPO} ${SOURCE_REPO_BRANCH} --allow-unrelated-histories  # e.g. git pull source-repo feature-branch --allow-unrelated-histories

If an error occurs that says unknown option 'allow-unrelated-histories', update your Git version. The --allow-unrelated-histories option was added in Git 2.9, and many operating systems/distros ship with an older version of Git installed.

Remove the source repo as a remote and review the new history:

git remote rm ${SOURCE_REPO}  # e.g. git remote rm source-repo
git log

If you are not happy with the updated history, you can git reset to the last commit prior to adding the new history or delete the local copy of the destination repo, re-clone it, and run the previous commands again with different options.

If you are satisfied with updated history, push it to the destination repo’s original remote:

git push ${DESTINATION_REMOTE} ${DESTINATION_BRANCH}  # e.g git push origin destination-branch

🎉 – You have successfully migrated a branch from one Git repository to another!