Split a subdirectory into its own repository
How do I extract a folder from my repo into a separate repo with its history?
Short answer
git filter-repo --path <dir> --path-rename <dir>/: keeps only that directory and moves it to the root, preserving every commit that touched it. Work on a fresh clone so your original repository is never at risk.
git clone repo extracted
cd extracted
git filter-repo --path packages/ui/ --path-rename packages/ui/:
Extract one directory into a standalone repo, history intact
Does this match your situation?
- A package inside your monorepo should become its own project.
- You want to open-source one folder without exposing the rest.
- A shared library needs its own release cycle.
Step-by-step fix
Work on a throwaway clone
filter-repo rewrites everything irreversibly. Doing it in a fresh clone means your original repository is untouched no matter what happens.
step 1git clone https://github.com/you/monorepo.git ui-extracted cd ui-extractedInstall the tool
filter-repo is the officially recommended replacement for filter-branch, and dramatically faster on any real repository.
step 2pip install git-filter-repoKeep only the directory you want, at the root
The first flag discards every other path from history; the rename moves what's left up to the repository root so it stands alone.
step 3git filter-repo --path packages/ui/ --path-rename packages/ui/: git log --oneline | head lsOnly commits that touched that path survive - the history is now genuinely about this project alone.
Point it at a new remote and push
filter-repo removes remotes deliberately, so you can't force-push over the original by accident.
step 4git remote add origin git@github.com:you/ui-library.git git push -u origin main git push --tagsRemove the directory from the original repo
Once the new repository is verified, delete the folder normally and depend on the extracted package instead. A plain deletion keeps the old history readable.
step 5cd ../monorepo git rm -r packages/ui git commit -m "Extract UI library into its own repository" git push
Why this works
Every commit references a tree describing the whole project, so 'a folder's history' isn't stored separately — it has to be reconstructed. filter-repo walks every commit, rebuilds its tree with only the paths you kept, drops commits that then contain no changes, and rewrites the parent links. The result is a genuine, coherent history for that directory alone, with entirely new hashes because every commit object was rebuilt.
If that didn’t work
- git subtree split --prefix=packages/ui -b ui-only is a built-in alternative that needs no extra install.
- If you only need the files and not the history, copying them into a fresh repo is far simpler.
- For a directory that moved during its life, pass every historical path so the earlier commits are kept too.
How to stop it happening again
- Keep independently releasable code in its own repository from the start.
- Or commit to a monorepo properly, with tooling that understands it.
- Avoid moving directories around unnecessarily - it complicates any later extraction.
Commands used in this guide
git filter-repoRewrite an entire repository's history - the supported way to purge a leaked secret or a huge file.
git subtreeVendor another repository into a subdirectory - without submodules' friction.
git cloneCopy a remote repository, its full history and its branches, onto your machine.
git pushUpload your commits to the remote repository.
git rmDelete a file from the repository - or just stop tracking it.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
How do I split a folder out of a Git repo with its history?
Clone the repo, then run git filter-repo --path <dir>/ --path-rename <dir>/: to keep only that directory and move it to the root. Add a new remote and push. Always do this in a throwaway clone, since filter-repo rewrites history irreversibly.
Does the extracted repository keep the original commits?
It keeps the commits that touched that directory, with their original messages, authors and dates - but every hash changes, because each commit object is rebuilt with a new tree and new parents.
Can I do this without installing git-filter-repo?
Yes - git subtree split --prefix=<dir> -b <branch> is built in and produces a branch containing just that directory's history, which you can push to a new repository. filter-repo is faster and more flexible, but subtree needs nothing extra.
Related rescue guides
How do I combine two separate repos while keeping both histories?
Add the second repository as a remote, fetch it, and merge with --allow-unrelated-histories into a subdirectory. Both sets of commits end up in one repository with their history intact.
Read the fix →Always fixableI committed a secret — how do I remove it from Git history?
Rotate the secret first — that's the only step that genuinely protects you. Then purge it from history with git filter-repo (or the BFG Repo-Cleaner) and force-push. Deleting the file in a new commit does nothing, because the old commit still contains it and is still fetchable.
Read the fix →Always fixableI committed a large file and my push is rejected — how do I remove it?
If it's still the last commit, git reset --soft HEAD~1 and re-commit without it. If it's further back, purge it from history with git filter-repo --strip-blobs-bigger-than. Going forward, route large binaries through Git LFS.
Read the fix →Always fixableMy .git folder is enormous — how do I find the biggest files in history?
git count-objects -vH tells you the total size; combining git rev-list --objects with git cat-file --batch-check lists the largest blobs by name. Deleting the files now won't shrink anything — they have to be purged from history.
Read the fix →