little cubes

Keeping git history when combining multiple repos into a monorepo

Use Case: You have multiple related repositories that have existed for a long time that you would like to combine into a single monorepo, without losing the long informational git history of each one of those repositories.

Decide on the structure of your monorepoSection titled: Decide on the structure of your monorepo

You can of course organize your monorepo in any way you wish. The standard way is to have a packages directory and sometimes an apps directory (I think distinguishing those two different kinds of packages is nice).

However you decide, we need to initialize the monorepo with these directories. If you have an existing monorepo you’re adding to, you can skip this step.

Terminal window
cd ~/dev
mkdir monorepo
cd monorepo
git init
git commit --allow-empty -m "Initial empty commit" # Can't merge with an empty repo
# Define whatever structure you want
mkdir packages
mkdir apps

Then for each repo you want to move:Section titled: Then for each repo you want to move:

1. Move all files except for .git into a subdirectorySection titled: 1. Move all files except for .git into a subdirectory

The goal is to move each file into the same directory structure that you want it to live in inside the monorepo.

So if I have a my-app repo, I’m going to move every file (except for the .git directory) into my-app/apps/my-app because I want its final resting place to be monorepo/apps/my-app.

~/dev/my-app
.git
.gitignore
package.json
src
index.ts

There’s nothing wrong with just moving these fils with your OS file explorer; that is the simpler approach and works just as well. But here are commands to do it with the terminal:

Terminal window
cd ~/dev/my-app
# Create directory(s) to match the monorepo structure.
# -p creates intermediate directories as required.
mkdir -p apps/my-app
# Move all files, including dot files.
# -k is used to ignore the error that apps/ can't be moved into itself.
git mv -k * .* apps/my-app

Then you’ll need to commit that change:

Terminal window
git commit -m "chore: Move all files into apps/my-app"

2. Merge the existing repos history into the monorepoSection titled: 2. Merge the existing repos history into the monorepo

Terminal window
cd ~/dev/monorepo
# Add your other local repo as a remote inside of the monorepo.
# -f fetches the repo state, needed to do the merge next,
# You can also use a github/ButBucket remote instead of a local one.
git remote add -f my-app ../my-app
# Merge the monorepo main branch into the my-app main branch.
git merge my-app/main --allow-unrelated-histories

You’re totally done with this repo now, so you can remove the remote you added earlier:

Terminal window
git remote remove my-app

Now just repeat steps 1 and 2 for every repo you’d like to move over!

Acknowledgements

This post is largely a repackaging of a Medium article to add clarity and prevent it from being hidden behind a paywall. Big thank you to Chris Thompson for writing that.