Clean up historyAlways fixable

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.

quick fix
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

  1. 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 1
    git clone https://github.com/you/monorepo.git ui-extracted
    cd ui-extracted
    
  2. Install the tool

    filter-repo is the officially recommended replacement for filter-branch, and dramatically faster on any real repository.

    step 2
    pip install git-filter-repo
    
  3. Keep 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 3
    git filter-repo --path packages/ui/ --path-rename packages/ui/:
    
    git log --oneline | head
    ls
    

    Only commits that touched that path survive - the history is now genuinely about this project alone.

  4. 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 4
    git remote add origin git@github.com:you/ui-library.git
    git push -u origin main
    git push --tags
    
  5. Remove 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 5
    cd ../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-repo

Rewrite an entire repository's history - the supported way to purge a leaked secret or a huge file.

git subtree

Vendor another repository into a subdirectory - without submodules' friction.

git clone

Copy a remote repository, its full history and its branches, onto your machine.

git push

Upload your commits to the remote repository.

git rm

Delete 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

← All Git Rescue guides