Merging separate Git repos

Code Computerlove
4 min readMar 7, 2023

--

Maybe you’ve been reading about monorepos. Maybe you’ve got a service that no longer needs to be scaled separately of the main site. Maybe someone got a bit too excited after reading about microservices and now you need to bring it all back to a monolith. Maybe your initial architecture was just plain wrong.

Whatever the reason, you have two (or more) repos that need to be merged into one. But the repos have been around for years with hundreds or thousands of commits, and you want to keep that history. And not just for nostalgia, those commits provide an audit trail tying every changed line of code back to a feature request or bug fix. You are referencing ticket numbers in commits right?

Fortunately, Git is awesome, and we can make this work with a little creative thinking — and a Git command flag you might not be aware of.

Giving credit where credit is due, someone has already written about this. So what follows is mostly their solution with a couple of tweaks to account for slight changes in Git over the years, and the fact that I’m on a Windows machine.

The first and most obvious problem we’ll encounter is the possibility of collisions with filenames. This can be easily avoided by moving each repo down a level. When we’re done the merged repo will look like this:

new-repo-root/ 
├─ repo-one/
│ ├─ repo-one-files
│ ├─ ...
├─ repo-two/
│ ├─ repo-two-files
│ ├─ ...

If you’re going for a monorepo then this sort of directory layout might even be what you’re looking for. If not, then once everything is in the one repo you’ll be able to push and pull it around until it’s the right shape.

To start, we’ll check out one of the repos.

C:\projects>git clone <repo-one-remote-address> merged-repo

Then move all the files down a level. Unfortunately, I’m on Windows so trying to wildcard git mv proved to be a little troublesome but I found some help on Stack Overflow.

C:\projects\merged-repo>mkdir repo-one
C:\projects\merged-repo>FOR %F IN (*) DO IF NOT %F == repo-one git mv %F repo-one
C:\projects\merged-repo>FOR /D %D IN (*) DO IF NOT %D == repo-one git mv %D repo-one
C:\projects\merged-repo>git commit -m "Moved repo-one down a level"

Now comes the not so intuitive bit. We’ll add the second repo as a new remote and fetch its branches.

C:\projects\merged-repo>git remote add repo-two-remote <repo-two-remote-address>
C:\projects\merged-repo>git fetch repo-two-remote

We’ll get a warning about there being no common commits, but it works. Now we can branch off repo-two and switch to it.

C:\projects\merged-repo>git switch -c branch-for-merging repo-two-remote/main

And move all the files in repo two down a level like we did in repo one.

C:\projects\merged-repo>mkdir repo-two 
C:\projects\merged-repo>FOR %F IN (*) DO IF NOT %F == repo-two git mv %F repo-two
C:\projects\merged-repo>FOR /D %D IN (*) DO IF NOT %D == repo-two git mv %D repo-two
C:\projects\merged-repo>git commit -m "Moved repo-two down a level"

Unlikely though it seems, we now are in the weird state where on the main branch we have:

new-repo-root/  
├─ repo-one/
│ ├─ repo-one-files
│ ├─ ...

And on the other branch we have:

new-repo-root/  
├─ repo-two/
│ ├─ repo-two-files
│ ├─ ...

Even though they’re from separate repos!

All that remains is to switch back to main and merge the repo-two branch in but by default that’ll throw a wobbler.

C:\projects\merged-repo>git switch main
C:\projects\merged-repo>git merge branch-for-merging
fatal: refusing to merge unrelated histories

But since we know exactly what we’re doing (🤞) we can override Git in this instance:

C:\projects\merged-repo>git merge branch-for-merging --allow-unrelated-histories

And boom, we’ve arrived where we wanted:

new-repo-root/  
├─ repo-one/
│ ├─ repo-one-files
│ ├─ ...
├─ repo-two/
│ ├─ repo-two-files
│ ├─ ...

A little bit of clean-up of branches and remotes and we’re done.

C:\projects\merged-repo>git branch -D branch-for-merging
C:\projects\merged-repo>git remote remove repo-two-remote

Through the power of Git, even something as major as merging multiple repos is relatively straight forward. With both code bases in the same repo, the refactoring can begin!

--

--

Code Computerlove

Code Computerlove are a digital product agency, making brilliant digital experiences since 1999.