Chapter 45: Git LFS
Git LFS (Git Large File Storage)
This is the tool that solves one of the most painful and most common real-world Git problems:
“My repo is 2 GB because of images/videos/PSDs/models and Git is dying — what do I do?”
Almost every developer who works with designers, game devs, ML engineers, video editors, photographers, or anyone who touches binary files larger than a few MB eventually hits this wall.
Today I’m going to explain Git LFS like I’m sitting next to you installing it and using it — very slowly, with real commands, real outputs, the exact mental model, when/why to use it, and a complete realistic example you can follow right now.
1. What problem does Git LFS solve? (the painful truth)
Normal Git stores every version of every file forever in the repository history.
- You commit a 50 MB PSD file → Git saves all 50 MB
- You edit it slightly → Git saves another ~50 MB (delta compression helps a bit, but not much for binaries)
- After 20 versions → your repo is suddenly 1 GB+ even though current files are only 200 MB
Worse:
- Every clone downloads the entire history → new teammates wait 30+ minutes to clone
- Every git push / git pull sends/receives huge amounts of data
- GitHub free plan has soft 1 GB repo limit and 5 GB push limit — you hit it fast
- Git operations become slow (status, log, blame on binary files)
Git LFS fixes this by replacing large files in history with tiny pointer files.
Instead of storing the 50 MB PSD in every commit, Git LFS stores:
- A pointer file (~100 bytes) that says “the real 50 MB file lives on LFS server at this URL + hash”
- The actual large file is uploaded to a separate LFS server (GitHub provides free LFS storage up to generous limits)
Result:
- Repo stays small (only pointers in history)
- Clone is fast
- Push/pull is fast
- You still version large files — just the content lives outside the Git database
2. How Git LFS works (mental model)
Normal Git commit:
|
0 1 2 3 4 5 6 7 8 |
commit abc1234 tree: ... blob: 50MB image.psd ← full file inside Git |
With LFS:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
commit abc1234 tree: ... blob: 120 bytes image.psd ← pointer file version https://git-lfs.github.com/spec/v1 oid sha256:abc...123 size 52428800 |
→ Real 50 MB file is uploaded to GitHub LFS storage → Git only keeps tiny pointer + hash + size
When you checkout:
- Git sees pointer → downloads real file from LFS server
- You see normal 50 MB image.psd in working directory
3. Realistic Example – Use Git LFS Right Now
Goal: Add large image files to a repo without bloating it
Step 1 – Install Git LFS (one-time)
Windows/macOS/Linux:
|
0 1 2 3 4 5 6 7 |
# Official way (recommended 2026) git lfs install |
(If it says command not found → install from https://git-lfs.com or package manager)
You’ll see:
|
0 1 2 3 4 5 6 |
Git LFS initialized. |
Step 2 – Create demo repo
|
0 1 2 3 4 5 6 7 8 |
mkdir git-lfs-demo cd git-lfs-demo git init |
Step 3 – Tell Git LFS which files to track
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Track common large file types git lfs track "*.png" git lfs track "*.jpg" git lfs track "*.jpeg" git lfs track "*.gif" git lfs track "*.psd" git lfs track "*.ai" git lfs track "*.mp4" git lfs track "*.mov" git lfs track "*.zip" git lfs track "*.pdf" # if you version PDFs |
This creates/updates .gitattributes:
|
0 1 2 3 4 5 6 7 8 |
*.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text ... |
Commit it:
|
0 1 2 3 4 5 6 7 |
git add .gitattributes git commit -m "chore: enable Git LFS for large binary files" |
Step 4 – Add some large files
Download or create some big files (for demo you can use any >1 MB image/video):
|
0 1 2 3 4 5 6 7 8 |
# Example: pretend we have these cp ~/Pictures/big-poster.png . cp ~/Videos/demo-video.mp4 . |
Step 5 – Add & commit normally
|
0 1 2 3 4 5 6 7 |
git add big-poster.png demo-video.mp4 git commit -m "Add large poster image and demo video" |
What really happens under the hood:
- Git sees they match LFS patterns
- Instead of storing full files → uploads them to GitHub LFS
- Commits tiny pointer files
|
0 1 2 3 4 5 6 |
git push origin main |
→ You’ll see Git LFS upload progress:
|
0 1 2 3 4 5 6 |
Git LFS: (2 of 2 files) 100% |████████████████████████████████████████| ... |
Now on GitHub:
- Repo size stays tiny (~few KB for pointers)
- But when someone clones → Git LFS automatically downloads the real large files
Step 6 – Verify
Clone fresh copy:
|
0 1 2 3 4 5 6 7 8 9 10 |
cd .. git clone https://github.com/your-username/git-lfs-demo.git fresh-clone cd fresh-clone ls -lh # you see big-poster.png and demo-video.mp4 are full size! |
4. Common .gitattributes + LFS Patterns (copy-paste 2026)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# Images & design files *.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text *.webp filter=lfs diff=lfs merge=lfs -text *.psd filter=lfs diff=lfs merge=lfs -text *.ai filter=lfs diff=lfs merge=lfs -text *.sketch filter=lfs diff=lfs merge=lfs -text # Videos & media *.mp4 filter=lfs diff=lfs merge=lfs -text *.mov filter=lfs diff=lfs merge=lfs -text *.avi filter=lfs diff=lfs merge=lfs -text *.mkv filter=lfs diff=lfs merge=lfs -text # Archives & binaries *.zip filter=lfs diff=lfs merge=lfs -text *.tar.gz filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text *.dll filter=lfs diff=lfs merge=lfs -text # ML models & large data *.h5 filter=lfs diff=lfs merge=lfs -text *.pth filter=lfs diff=lfs merge=lfs -text *.pt filter=lfs diff=lfs merge=lfs -text *.onnx filter=lfs diff=lfs merge=lfs -text *.safetensors filter=lfs diff=lfs merge=lfs -text |
5. Important Limits & Costs (2026 reality)
GitHub LFS (free tier):
- 1 GB storage per repo
- 1 GB monthly bandwidth per repo
- Soft limits — polite warnings after that
Pro / Team / Enterprise:
- Much higher limits (10–50 GB storage, more bandwidth)
- Or use third-party LFS servers (GitLab, Azure, MinIO, etc.)
6. Common Gotchas & Fixes
- Forgot to run git lfs install → LFS doesn’t work → teammates clone but get pointer files instead of real files
- Already committed large files → LFS doesn’t retroactively shrink history → Need git lfs migrate or git filter-repo to migrate existing large files
- Teammate cloned without LFS installed → they see pointers → remind them to git lfs install
Got the Git LFS feeling now?
Git LFS = “Git for large binary files” — keeps your repo small & fast while still versioning images, videos, models, PSDs, etc.
It’s standard in any project that touches media/design/ML.
Next?
- Want to migrate an existing bloated repo to LFS?
- See how to set up custom LFS server if GitHub limits are too small?
- Or do one final big summary of all Git concepts we covered today?
Just tell me — we’ll finish strong. You’ve gone from zero to very advanced Git — incredible work! 🚀
