I had created some tools inside a monorepo and I wanted to move them out to their own repo, preserving the full commit history.

The repo has just one branch (master branch), which makes things a bit simpler, and the project I want to move out is contained in its own folder.

Step 1 - Clone the old repo

The old repo is called monorepo and the new one will be called fancy-tool. Coincidentally, that is also the name of the subfolder where the code lives.

I’m cloning it out of my local working directory:

git clone monorepo fancy-tool

The local working directory will appear as the origin:

$ git remote -v
origin  C:/Users/ngeor/Projects/github/monorepo (fetch)
origin  C:/Users/ngeor/Projects/github/monorepo (push)

And I’m removing the origin with git remote remove origin to make sure I don’t accidentally push something at this point.

Step 2 - Filter history

The following command needs to be run against the new local repo (fancy-tool). It will rewrite the git history, keeping only the information that is relevant to the subfolder fancy-tool. Additionally, that subfolder will become the root folder.

git filter-branch --prune-empty --tag-name-filter cat --subdirectory-filter fancy-tool -- --all

Step 3 - Delete tags that are irrelevant

I didn’t have any branches, other than the master branch, but I did have some tags. I had to go over them to make sure I delete tags that are not relevant anymore (with git tag -d).

At this point I also run git gc && git prune (because the internet said so, not 100% sure if it’s needed).

Step 4 - Push to new remote

The repo is ready, time to share it with the world. I created a new repo in GitHub. The important thing here is to make sure it gets created with no commits (i.e. opt out of the README file when creating the repo).

git remote add origin repo-url
git push -u origin master
git push --tags

Step 5 - Remove from original repo

Now that the fancy-tool repo has its own home, I wanted to remove it from its old place in the monorepo. This requires rewriting the history of an existing public repo, which would have been difficult on the other users, had there been any.

git filter-branch --tree-filter "rm -rf fancy-tool" --prune-empty HEAD
git push --force-with-lease

As before, I had to get rid of the irrelevant tags, but this time I also had to remove them from origin (so not only git tag -d but also git push --delete origin).