Safeguard
Developer Tools•

How do you import repos into a monorepo with git filter-repo?

Import each repo into a monorepo subdirectory with git filter-repo: rename tags, move paths, check for secrets first, and verify what history you kept.

Safeguard Security Team
4 min read

To import a repository into a monorepo and keep its history, work in a fresh clone of the source repo, use git filter-repo to move every path under a new subdirectory, rename its tags so they do not collide, then merge that rewritten history into the monorepo. Check for secrets and large files before you merge.

What filter-repo actually does

git filter-repo rewrites a repository's history using filters you choose. Its documentation describes it as a destructive operation that writes new commits, trees, tags and blobs, then deletes the original history. It lists the use cases a monorepo migration needs: moving all files into a subdirectory in preparation for merging with another repo, and renaming tags, also often in preparation for merging with another repo.

It also has a safety check. It aborts if you run it from a repo that is not a fresh clone, to prevent losing local history that exists nowhere else. Treat that as a rule for your own process: always work on a throwaway clone, never the only copy.

The steps

  1. Make a fresh clone of the source repo into a scratch directory. Never run the rewrite in the original.
  2. Run the analysis mode first. filter-repo can generate reports that help you decide what to filter, such as unusually large blobs.
  3. Move the files. The to-subdirectory-filter option treats the project root as if it were under the directory you name. For a service called payments, that means every path becomes services/payments/....
  4. Rename tags and branches. Two repos that both have a v1.0.0 tag will collide. A tag rename with a service prefix keeps them apart.
  5. Add the rewritten clone as a remote of the monorepo, fetch, and merge with a merge commit that allows unrelated histories.
  6. Check the result: file counts, a sample of git log for a few paths, and a build.

A sketch of step 3, run in the scratch clone:

git filter-repo --to-subdirectory-filter services/payments \
  --tag-rename '':'payments-'

The empty string before the colon means "any tag name that starts with nothing", so every tag gets the prefix. Run it on a test clone first and confirm the tag names before you rely on it.

What changes when history is rewritten

Rewritten commits get new hashes. filter-repo rewrites abbreviated hashes that appear inside commit messages so they point at the new commits, and the preserve-commit-hashes option turns that off. Anything outside the repo that references an old hash, such as a ticket or a wiki page, will not match unless you keep a mapping.

Because commits are new objects, assume that signature data attached to the originals does not carry over as a valid signature on the new commits. Check this with your own tooling on a sample commit rather than assuming, and tell reviewers what to expect. If commit signing matters for your audit evidence, keep the archived source repo readable so the original signed history still exists.

Decide what not to import

Full history is not always worth the cost. Options include importing only the last year of history, or importing nothing and linking to the archived repo. Large binaries are the usual reason to filter. filter-repo has an option to strip blobs bigger than a size you set. Secrets are the other reason, and they get their own treatment in do secrets in old repo history follow you into the monorepo.

After the import

Run a dependency inventory so you know what the merged repo actually contains. Our guide to finding every lockfile in a monorepo covers that step. Then rebuild ownership and branch rules, covered in CODEOWNERS paths after moving a repo.

Safeguard's SBOM and dependency scanning can build the inventory of the merged repo, and you can try a free scan at gold.safeguard.sh/scan.

Try it yourself

Click through it at your own pace, or open it full screen.

Click through the inventory and notice how many manifests a merged repo can contain compared with the single one you started with.

Quick check

Do I have to keep full history when I move to a monorepo?

No. You can import a limited window of history or none, and keep the old repo archived and linked. The trade is that blame and bisect across the boundary need the old repo.

Can I run filter-repo on my working clone?

It refuses to run on a repo that is not a fresh clone, by design. Use a separate clone so a mistake never costs you unpushed work.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.