Taming a 486MB Git Repo
Contents
I recently cloned a repository and noticed it was 486MB. The actual source code? About 6MB. That’s a 98.7% overhead. Here’s how I found the bloat and fixed it.
Where Did the Space Go?
The first step is understanding where the bytes are. Two commands tell you most of the story:
|
|
So 285MB was git history and 194MB was the working tree — mostly images. The code, templates, and config files were about 6MB total.
Finding the Largest Files
Git stores every version of every file as a blob. To find the biggest ones:
|
|
Output (trimmed):
|
|
The pattern was clear: raw camera photos at 7-10MB each. Some were treatment photos used on the site. Others were in a temp/ folder that had already been deleted.
The Ghost Files Problem
Here’s the thing that surprises people: deleting a file from git doesn’t reclaim the space.
Six photos (~50MB total) had been removed from public/images/temp/ months ago. They didn’t exist in the working directory anymore. But git faithfully preserved every byte in its object database — because that’s what git does. It keeps the full history so you can go back to any commit.
Running git rm and committing only adds a new commit that says “this file no longer exists.” The old blob stays in .git/objects/ forever.
|
|
Finding Orphaned Images
Beyond deleted files in git history, there were images on disk that no code referenced anymore. A quick check:
|
|
This found five treatment images (~35MB) that were commented out in the data file but still sitting on disk.
The Fix: Compression First, Then History
My initial instinct was to reach for Git LFS. But I stopped and thought about it:
The images were 7-10MB each because they were unprocessed camera photos. No web page needs a 10MB JPEG. The site was already slow because browsers had to download these massive files.
Step 1: Compress the images
|
|
Results:
|
|
192MB of images became ~5MB. A 97% reduction, and the site got faster too.
Step 2: Remove deleted files from history
Now for the 285MB git database. git filter-repo is the modern replacement for the older git filter-branch:
|
|
After a git gc --aggressive:
|
|
Step 3: Remove orphaned images and commit
|
|
Do You Actually Need Git LFS?
Git LFS (Large File Storage) replaces large files in your repo with small pointer files. The actual content is stored on a separate LFS server. It’s the go-to recommendation for large binary files.
But consider the trade-offs:
| Git LFS | Compression | |
|---|---|---|
| Setup complexity | Everyone needs git-lfs installed |
One-time conversion |
| Cost | GitHub: 1GB free, then $5/50GB/month | Free |
| Clone speed | Fast clone, then LFS fetches on checkout | Fast clone, small files |
| CI/CD | Needs LFS configured in CI | Just works |
| Offline access | Needs LFS server for file content | Everything is local |
| History rewrite | git lfs migrate rewrites history |
git filter-repo rewrites history |
When LFS makes sense
- Binary assets that can’t be compressed (compiled binaries, fonts, videos)
- Files that change frequently (design files, large datasets)
- Large teams where you want to control who downloads what
When compression is the better answer
- Images for a website — they should be optimized anyway for page speed
- One-time cleanup — if the files won’t be re-added at their original size
- Small teams / personal projects — LFS adds complexity you don’t need
In our case, compression was clearly the right call. The images needed to be smaller for the website to perform well. LFS would have just hidden the problem — visitors would still be downloading 10MB JPEGs.
The hybrid approach
If you do expect large binaries in the future, set up LFS after cleaning up:
|
|
And add a pre-commit hook to catch oversized files before they enter history:
|
|
The Caveats
Rewriting history with git filter-repo (or git lfs migrate) is a destructive operation:
- You’ll need to force-push to the remote
- All collaborators must re-clone (their local history won’t match)
- Open PRs will have conflicts
- Commit hashes change — any references to specific commits break
For a small team or personal project, this is fine. For a large team, coordinate first. Do it on a weekend. Announce it. Have everyone push their branches before you rewrite.
The Result
| Before | After | |
|---|---|---|
.git directory |
285 MB | 12 MB |
| Working directory | 194 MB | 5 MB |
| Total | 486 MB | 17 MB |
| Clone time | ~10 min | < 10s |
| Largest image | 10 MB | 84 KB |
| Page load (images) | Painful | Fast |
A 96% reduction in repo size, and the website got significantly faster as a side effect.
The lesson: before reaching for Git LFS, ask yourself — should these files be this large in the first place?