reduce .git size
Basic stuff
Check how big your repo actually is before doing anything:
du -sh .git
git count-objects -vcount-objects -v breaks down loose vs packed objects and their sizes — useful for confirming whether cleanup actually helped afterward.
git gc / prune
Standard housekeeping — clears reflog entries and repacks/prunes unreachable objects:
git reflog expire --expire=now --all
git gc --prune=now --aggressiveNote: this won’t remove large files that are referenced by some commit, it only cleans up objects that are already dangling. For actual history rewriting, you need filter-repo (steps 4–5).
Identifying big files
Find the largest blobs across all of history, not just the current tree:
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
sed -n 's/^blob //p' |
sort --numeric-sort --key=2 --reverse |
head -20 |
cut -c 1-12,41- |
numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearestgit-filter-repo for history manipulation
Install git-filter-repo first.
filter-repo might fail to run, since it expects to run on a fresh clone by default (safety check, since it’s destructive). Use --force to run in-place on an existing clone.
And git filter-repo removes your origin, since it edits the whole history, and you have to force push your local repo.
Using git-filter-repo on a directory
git filter-repo --path dir/to/remove/ --invert-pathsRemoves the whole directory from every commit in history.
Using git-filter-repo on a file (or multiple files)
git filter-repo --invert-paths \
--path path/to/file-one.png \
--path path/to/file-two.woff2Repeat --path per file. Stack as many as needed in one pass rather than running filter-repo multiple times — every run rewrites all commit hashes again.
Post-filter cleanup and push
filter-repo strips the origin remote automatically (another safety measure) — re-add it:
git remote add origin <your-repo-url>Then force-push, since history has been rewritten:
git push origin --force --all
git push origin --force --tagsGotchas
- All downstream commit hashes change. Anyone with an existing clone needs to re-clone or hard-reset onto the new history — pulling/merging normally will conflict badly.
- Add removed paths to
.gitignoreif you don’t want them accidentally re-committed later. - If you’re rewriting in stages (multiple filter-repo runs) and already pushed in between, collaborators need to re-sync after each push. It’s better to cleanup all at once and then push, making it easier for everyone.
- For ongoing large-file needs (not just cleanup) — binary assets you actually want to keep versioned — consider Git LFS instead of committing them raw.