How to merge git repositories preserving git history
Struggling with merging multiple repositories together into one (mono) repository? You wanna preserve your valuable git history?
This blogpost will show you how to do this step-by-step.
Let’s assume we have two repositories.
-
RepoA
-
RepoB
Next to these two repositories we have a new MonoRepo
.
All in the same folder.
The two existing repositories need to be merged into a new monorepo MonoRepo
where we expect the following structure:
Modify old repository
First, we need to make some (local) changes to our old RepoA
.
In this RepoA
we will be preparing a move to the subdirectory.
Modify the old repository to prevent merge conflicts |
If we perform some preparation in the old repo(s), we will avoid getting merge conflicts in the resulting MonoRepo
.
If 2 the same file exist in RepoA
and RepoB
it will cause a merge conflict if we try to merge them both into the root of MonoRepo
.
cd RepoA (1)
git checkout master (2)
git pull (3)
git checkout -b prepare_monorepo (4)
mkdir -p repo-a (5)
shopt -s extglob (6)
git mv -k !(repo-a) repo-a (7)
shopt -u extglob
git rm -f --ignore-unmatch .gitattibutes (8)
git rm -f --ignore-unmatch .gitignore (8)
git rm -f --ignore-unmatch .editorconfig (8)
git commit -m "Preparing for monorepo merge" (9)
1 | Switch to RepoA directory |
2 | We can only merge one branch of both RepoA and RepoB , in this case master |
3 | Update with all remote changes |
4 | Create a new branch prepare_mono |
5 | Create a subdirectory repo-a .
This directory is the directory in which the content will end-up in MonoRepo . |
6 | Extended globbing allows pattern matching |
7 | Move everything into repo-a , excluding the directory repo-a itself |
8 | Some files are not moved with the git mv command. .gitattributes , .gitignore and .editorconfig are a good example.
Since we will have a complete new repository MonoRepo , it will have these files already.
Therefore, we need to remove them from the old repository. |
9 | Commit the change to RepoA .
There is no need to push it to the remote, since we only use this change locally to merge in the next step. |
It is important to use git mv and not a regular mv to instruct git .
Otherwise, we will create a remove & create of the files.
|
Merge into new repository
Next we need to switch to the MonoRepo
where the actual merge is performed.
cd ../MonoRepo (1)
git remote add -f RepoA ../RepoA (2)
git merge -m "Integrating RepoA" RepoA/prepare_monorepo --allow-unrelated-histories (3)
git remote rm RepoA (4)
git push (5)
1 | Switch to the MonoRepo directory |
2 | Add the directory ../RepoA as a remote with name RepoA |
3 | Perform the actual merge with unrelated histories |
4 | Remove the remote RepoA we created earlier |
5 | Push the merge history to the origin remote of MonoRepo |
(Optionally) remove the temporary branch
We can clean up the local branch in the old repository to finish everything up.
cd ../RepoA
git checkout --force master
git branch -D prepare_monorepo
cd ../
Now repeat all the steps for RepoB!