Splitting a large Git mono-repository into several smaller repositories is a bit like the old saying about cake: you want to have your cake and eat it too. As repositories grow, they become harder to manage—builds take longer, pipelines get more complex, and you may find yourself fixing issues in parts of the codebase unrelated to your work just to get your build to pass.

If this sounds familiar, you’re not alone. Eventually, the need arises to split a mono-repo. Having several smaller, focused repositories can simplify workflows and help teams take ownership of their code. But how do you do this without losing years of valuable history? Simply starting a new repo and ignoring the past isn’t ideal, and forking then deleting unrelated files leaves too much baggage. Is there a better way?

Let’s walk through the process. Git is a powerful tool, and with the help of some scripts, you can split a mono-repo while preserving the history of specific files.

Requirements

You’ll need the git-filter-repo1 Python script. Install it with:

Install git-filter-repo
brew install git-filter-repo
# or
pip3 install git-filter-repo

Clone the original repository

Start by cloning your original repository under a new name:

Clone your repository
git clone ssh://git@github.com/my/repo.git new-beginning

Disconnect from the old origin

Switch to your new clone and remove the old remote:

Remove old remote
cd new-beginning
git switch -c feature/separate-xyz
git remote remove origin

Filter history to keep only what You need

List the files in your new repo:

List files in repo
ls -1

Decide which files you want to keep, then filter the history:

Filter history and keep only selected files
git filter-repo \
    --force \
    --path .gitattributes \
    --path .gitignore \
    --path .pre-commit-config.yaml \
    --path CHANGELOG.md \
    --path CONTRIBUTING.md \
    --path Makefile \
    # add other paths as needed

Clean up unreferenced files and commits

Run garbage collection to clean up:

Run garbage collection
git gc --aggressive

Set Up a New Origin

Create a new repository (if you haven’t already) and point your filtered repo to it:

Add new remote and switch to main
git remote add origin ssh://git@github.com/my/new-beginning.git
git switch -c main

Warning

Depending on your automation and pipelines, pushing changes may trigger jobs or require updates to paths and names. Double-check everything before pushing.

If you need, modify any pipelines or automation to prevent unintended builds or release.

Push Your changes

Push your changes to the new repository:

Push changes to new repository
git push -u origin HEAD:main
git push --tags
# If you need to overwrite history (be very careful!):
git push --force

Final Steps

Commit any remaining changes, push, and verify that everything works as expected.

Following this tutorial, you will end up with a new repo, with full history of changes in files that you listed and only those. It’s the cleanest way to achieve it, that I found.

“The real takeaway: You don’t have to lose your project’s history or clutter your new repo with old baggage. With the right tools and a careful approach, you can split a mono-repo cleanly and keep your workflow efficient.”


Enjoyed this post? Buy Me a Coffee at ko-fi.com