Large Files and Git LFS
Git is built for text: source code, configuration, documentation. Large binary files — videos, design files, datasets, game assets — make repositories slow to clone, because every version of every file is kept forever. Git LFS (Large File Storage) solves this by storing big files elsewhere and keeping only small pointers in Git.
Why big files hurt
- History keeps every version. Commit a 200 MB video and then a slightly edited version, and the repository holds 400 MB — for everyone who clones it, forever, even after you delete the file.
- Binary files don't compress well against each other the way text does, and their diffs are meaningless.
- Hosts have limits. GitHub warns about files over 50 MB and rejects files over 100 MB in normal Git pushes, and recommends keeping repositories small.
First line of defence: don't commit them
Many large files shouldn't be in version control at all: build output, dependencies, logs, local databases, downloaded datasets. Ignore them (lesson 14) and document how to produce or download them instead. Before committing, check for anything unexpectedly large with git status and git diff --staged --stat.
Git LFS
When large files genuinely belong with the project — images for a website, models for a game — use Git LFS. It's a separate extension, installed once per computer:
# macOS brew install git-lfs # Debian/Ubuntu sudo apt install git-lfs # Windows: included with Git for Windows git lfs install # one-time setup for your user account
Tracking files with LFS
Tell LFS which files to handle, by pattern. This writes the rules into .gitattributes, which you commit:
git lfs track "*.psd" git lfs track "assets/videos/*.mp4" git add .gitattributes git commit -m "Track design files and videos with Git LFS"
*.psd filter=lfs diff=lfs merge=lfs -text assets/videos/*.mp4 filter=lfs diff=lfs merge=lfs -text
From then on you use Git as usual: git add, git commit, git push. Behind the scenes, Git stores a tiny pointer file in the repository, and the real content is uploaded to the LFS storage of your host:
version https://git-lfs.github.com/spec/v1 oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393 size 12345
When someone clones or pulls, LFS downloads only the versions of large files needed for the commit they check out — not every historical version.
Useful LFS commands
| Command | Does |
|---|---|
git lfs track | List the patterns being tracked |
git lfs ls-files | List files currently stored in LFS |
git lfs pull | Download LFS content for the current checkout (e.g. after a clone with LFS skipped) |
git lfs migrate import --include="*.psd" | Rewrite existing history to move matching files into LFS (rewrites commits — coordinate with your team) |
git lfs locks, git lfs lock file | File locking, so two people don't edit the same unmergeable file at once |
Costs and limits
LFS storage and download bandwidth are usually metered separately by hosting services, with a free allowance and paid packs above it. Check your host's current limits before putting gigabytes of assets in LFS.
Removing a large file from history
If a huge file was committed by mistake and pushed, deleting it in a new commit doesn't shrink the repository — it's still in history. To truly remove it you must rewrite history with a tool such as git filter-repo (a separate, widely recommended tool that replaces the old git filter-branch), then force-push, and everyone must re-clone. If it hasn't been pushed yet, it's much easier: amend or interactively rebase the commit that added it (lessons 17 and 27).
Other options
- Partial clones (
git clone --filter=blob:none) download file contents on demand — helpful for big histories even without LFS. - Separate storage — keep datasets in object storage or a data-versioning tool, and store only a reference in Git.
Try it yourself
In a test repository, install Git LFS, track *.png, commit a large image, and run git lfs ls-files. Then look at what Git itself stored with git show HEAD:yourimage.png — you'll see the pointer file, not the image.