Robert Važan

How to merge Git repositories

In Git, you usually merge pull requests consisting of a few commits worth of changes. Sometimes however you need to merge two separate repositories with unrelated histories. This usually happens when an experimental component is first developed in a separate repository and later promoted to core component developed in-tree. Such merges are a bit more complicated than the usual pull request merges.

There are some SO questions (1, 2) and articles on various sites and blogs (for example 1, 2, 3), but all of them are wrong in subtle ways. I am not even mentioning the ones that are completely wrong. I have therefore decided to write down canonical repository merge process.

First the gotchas to be aware of:

So here's the promised canonical process. We will be merging repository "extension" into repository "core".

Step 1: Add extension repository as a remote in the core repository and fetch it. The remote is only temporary. It can be later removed. We will disable tag import right here to avoid tag conflicts.

cd /path/to/core-directory
git remote add --no-tags extension-remote /path/to/extension-directory
git fetch extension-remote

Step 2: Promote remote branch to tracking branch, so that we can make changes before merge. The tracking branch is only temporary. It can be deleted later.

git checkout -b extension-branch extension-remote/master

Step 3: Perform the necessary changes and commit. In most cases, you would move extension code into a subdirectory and delete files already present in the core project. The goal is to avoid conflicts during merge.

mkdir extension
mv src extension/
git stage .
git commit -m "Moved the extension into a subdirectory"

Step 4: Switch back to master and merge the extension branch.

git switch master
git merge --allow-unrelated-histories --no-commit extension-branch
git commit -m "Merged the extension"

Step 5: Clean up. Delete the extension branch and extension remote.

git branch -D extension-branch
git remote remove extension-remote

And we are done. If you look at the history of the core repository now, you will see that the head revision merges the two repositories and that commits from both histories are preserved.