Git LFS & Large Files
Reviewed & published by Brayan K
Big binaries — PSDs, datasets, videos — wreck a normal Git repo, because Git stores a full new copy on every change. Git Large File Storage keeps them out of your history, storing tiny pointers in the repo and the real files on an LFS server. This lesson covers installing, tracking, pointers, listing, migrating, and quotas.
Part of the free Git course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn
- Set up LFS with git lfs install
- Track file types with git lfs track and .gitattributes
- How pointer files replace binaries in history
- List managed files with git lfs ls-files
- Convert existing files with git lfs migrate
- Why big binaries bloat history, plus storage quotas
1️⃣ Installing & Tracking
Run git lfs install once to wire up LFS's filters and hooks, then git lfs track "*.psd" to manage a file type. Tracking writes a rule into .gitattributes — commit that file so everyone who clones the repo uses LFS for those patterns too.
# Git LFS is an extension — install it once per machine, then per repo.
# 1) One-time setup of the LFS filters and hooks for your account
git lfs install
# Updated Git hooks.
# Git LFS initialized.
# 2) Tell LFS which file types to manage in THIS repo
git lfs track "*.psd"
git lfs track "*.zip"
# This writes patterns into .gitattributes — commit that file!
cat .gitattributes2️⃣ Pointer Files: How LFS Stores Things
You git add and git commit large files as normal, but what Git actually stores in the repo is a tiny pointer file — an oid hash and a size. The real binary is uploaded to the LFS server on push and fetched back on checkout. That's how history stays small.
# Add and commit a large file as usual — LFS handles it transparently.
git add design.psd .gitattributes
git commit -m "Add design source (via LFS)"
# What Git actually STORED in the repo is a tiny POINTER, not the 200MB file:
git show HEAD:design.psd
# The real binary was uploaded to the LFS server on push; the pointer
# (an oid hash + size) is all that lives in normal Git history.3️⃣ Listing LFS Files
git lfs ls-files lists every file LFS manages, with its object id. Use it to confirm your big assets are genuinely going through LFS and weren't accidentally committed as normal blobs.
# Which files in this repo are managed by LFS? Ask LFS directly.
git lfs ls-files
# Each line shows the object id (shortened) and the file path.
# Use this to confirm your big assets are really going through LFS
# and aren't accidentally committed as normal blobs.4️⃣ Migrating Existing Files
Already committed big binaries the normal way before adding LFS? git lfs migrate import --include="*.psd" rewrites history so those files become LFS pointers. Because it rewrites commits (new hashes), treat it like a rebase: coordinate with your team and only force-push when everyone's ready.
# Already committed big binaries the NORMAL way before adding LFS?
# Rewrite history to convert them into LFS pointers.
# Convert all *.psd in history to LFS (this REWRITES commits!)
git lfs migrate import --include="*.psd"
# Because history changed, hashes change — coordinate with your team
# and force-push only when everyone is ready (just like a rebase).
git push --force-with-leaseYour turn. Install LFS, track a file type, and list managed files. Fill in the three blanks.
# 🎯 YOUR TURN — replace each ___ then run it in your terminal.
# Goal: set up LFS in a repo and track a big binary type.
# 1) Initialize LFS for your account (one-time)
git lfs ___ # 👉 the subcommand that sets up hooks/filters
# 2) Track all PSD files so LFS manages them
git lfs ___ "*.psd" # 👉 the subcommand that writes a rule to .gitattributes
# 3) Confirm which files LFS is now managing
git lfs ___ # 👉 the subcommand that lists LFS files
# ✅ Expected: .gitattributes gains a "*.psd filter=lfs" line, and after you
# add a PSD, "git lfs ls-files" lists it with an object id.Common Errors (and the fix)
- Teammates get pointer text, not the file: they haven't run git lfs install or lack the extension. Install LFS, then re-checkout.
- Tracking didn't apply for others: you forgot to commit .gitattributes. Commit it so the rules ship with the repo.
- Repo still huge after adding LFS: existing big files are already in history. Use git lfs migrate to convert them.
- Push rejected by quota: you've hit the host's LFS storage or bandwidth limit. Prune unused objects or upgrade the plan.
Pro Tips
- 💡 Track before you commit: add the LFS rule first so the file never lands in normal history.
- 💡 Commit .gitattributes: it's what tells every clone to use LFS for those patterns.
- 💡 Track narrowly: only route file types that truly need LFS to conserve quota.
- 💡 Verify with ls-files: confirm big assets are managed, not accidentally inline.
📋 Quick Reference
| Command | Purpose |
|---|---|
| git lfs install | Set up LFS filters and hooks (once) |
| git lfs track "*.ext" | Manage a file type via .gitattributes |
| git lfs ls-files | List files managed by LFS |
| git lfs migrate import --include | Convert existing history into LFS |
| git lfs prune | Remove old local LFS objects |
Mini-Challenge: Prove the Pointer
No commands given this time — just the plan. Show that LFS keeps a binary out of normal history.
# 🎯 MINI-CHALLENGE: prove LFS keeps a binary out of normal history.
# No commands given — just the plan. Run each step in your terminal.
#
# 1. In a fresh repo, run "git lfs install" and track "*.bin"
# 2. Commit .gitattributes, then add and commit a multi-MB .bin file
# 3. Run "git show HEAD:yourfile.bin" — confirm you see a POINTER, not bytes
# 4. Run "git lfs ls-files" — confirm the file is listed as LFS-managed
# 5. Reflect: how would repo size differ if you'd committed it without LFS?
#
# ✅ Expected: the repository stores a tiny pointer while the real binary
# lives in the LFS store, keeping normal history small.
# your commands here🎉 Lesson Complete!
- ✅ git lfs install then git lfs track sets up LFS via .gitattributes
- ✅ The repo stores a tiny pointer; the real binary lives on the LFS server
- ✅ git lfs ls-files shows what's managed; git lfs migrate converts old files
- ✅ Huge binaries bloat normal history because Git copies them on every change
- ✅ Watch storage and bandwidth quotas on hosted LFS
Practice quiz
What problem does Git LFS solve?
- Large binary files bloating normal Git history and repository size
- Merge conflicts in text files
- Branches that have diverged
- Slow network connections only
Answer: Large binary files bloating normal Git history and repository size. Git stores a full new copy of a binary on every change, so large files balloon the repository; LFS keeps them out of the normal history.
What does git lfs track "*.psd" do?
- Compresses PSD files
- Deletes all PSD files
- Records a rule in .gitattributes so matching files are handled by LFS
- Uploads PSDs immediately
Answer: Records a rule in .gitattributes so matching files are handled by LFS. git lfs track writes a pattern into .gitattributes telling Git to route matching files through LFS.
Which file must you commit for LFS tracking rules to take effect for collaborators?
- .git/config
- .gitattributes
- .gitignore
- .lfsconfig only
Answer: .gitattributes. Tracking rules live in .gitattributes; committing it ensures everyone cloning the repo uses LFS for those patterns.
What is actually stored IN the Git repository for an LFS-tracked file?
- Nothing at all
- A thumbnail image
- The full binary, compressed
- A small text pointer file referencing the object
Answer: A small text pointer file referencing the object. Git stores a tiny pointer file (an oid hash and size); the real binary lives on the LFS server.
Where does the real binary content of an LFS file live?
- On a separate LFS server/store, fetched on checkout
- In .gitignore
- Only in your browser cache
- Inside .git/objects like any commit
Answer: On a separate LFS server/store, fetched on checkout. LFS keeps the actual large object on a dedicated LFS store and downloads it when you check the file out.
Which command must you run once to set up the LFS hooks and filters?
- git lfs enable
- git lfs install
- git install lfs
- git lfs start
Answer: git lfs install. git lfs install configures the smudge/clean filters and hooks so LFS engages automatically.
How do you list which files in the repo are managed by LFS?
- git lfs show
- git ls-lfs
- git lfs status --all
- git lfs ls-files
Answer: git lfs ls-files. git lfs ls-files prints each LFS-tracked file along with its object id.
You have a repo whose history already contains large binaries committed normally. Which tool rewrites history to move them into LFS?
- git lfs pull
- git lfs prune
- git lfs migrate
- git lfs track
Answer: git lfs migrate. git lfs migrate rewrites existing history so previously committed large files are converted to LFS pointers.
Why do huge binaries committed the normal way bloat Git history so badly?
- Git refuses to compress them
- Git stores a complete new copy of the whole file on every change, and every clone carries all of them forever
- They corrupt the index
- They are stored twice on purpose
Answer: Git stores a complete new copy of the whole file on every change, and every clone carries all of them forever. Binaries don't delta well, so each edit adds another full copy that lives in history forever and ships with every clone.
What commonly limits how much you can store and transfer with a hosted LFS service?
- Storage and bandwidth quotas set by the hosting provider
- The length of commit messages
- The number of collaborators
- The number of branches
Answer: Storage and bandwidth quotas set by the hosting provider. LFS hosting typically enforces storage and bandwidth quotas, so large or busy repos can exceed plan limits.
Continue this course
- Previous: Multiple Working Trees with git worktree
- Next: How Git Works Internally — Blobs, trees, commits and the DAG — what a SHA really points to
- Quick reference: Git cheat sheet
Frequently asked questions
What is Git LFS and why would I use it?
Git Large File Storage (LFS) is an extension that keeps big binary files — design assets, datasets, videos, archives — out of your normal Git history. Plain Git stores a complete new copy of a file every time it changes, and binaries don't compress between versions, so a repo full of large assets balloons and every clone has to download all of it forever. LFS replaces those files in history with tiny pointers and stores the real content on a separate server, keeping the repository fast and small.
How do I start tracking files with LFS?
Run git lfs install once to set up the LFS filters and hooks, then git lfs track "*.psd" (or whatever pattern) to manage a file type. That track command writes a rule into .gitattributes, so you must commit .gitattributes for the setup to work for everyone who clones the repo. After that, you git add and git commit large files exactly as normal — LFS intercepts them transparently.
What actually gets stored in the repository for an LFS file?
Instead of the binary, Git stores a small text pointer file containing an oid (a sha256 hash of the content) and the file's size. The real bytes are uploaded to the LFS server when you push and downloaded again when you check the file out. You can see the pointer with git show HEAD:file, and you can list every LFS-managed file in the repo with git lfs ls-files.
I already committed large files normally — can I move them into LFS?
Yes. git lfs migrate import --include="*.ext" rewrites your existing history so previously committed large files become LFS pointers. Because this rewrites commits (and therefore changes hashes), treat it like a rebase: coordinate with your team and only force-push once everyone is ready. Migrating is the standard fix when a repo has already grown bloated from binaries committed before LFS was set up.
Are there limits I should know about with hosted LFS?
Yes — LFS storage lives on a server, and hosting providers typically enforce storage and bandwidth quotas as part of their plans. A repo with many or frequently-changing large assets can hit those limits, so it's worth tracking only the file types that truly need LFS, pruning old LFS objects you no longer need, and being mindful of how often huge files change. Quotas are usually the first thing teams bump into as LFS usage grows.