Git Large File Storage (LFS)
Git is designed for text — source code, configuration files, documentation. It stores every version of every file in history, which works brilliantly for text because diffs are tiny. Binary files like images, videos, compiled assets, and design files do not diff well — every version is stored in full. A 50 MB Photoshop file with 100 revisions consumes 5 GB of repository history just for that one file. Git LFS (Large File Storage) solves this by replacing large binary files in your Git history with tiny pointer files, while storing the actual binary data on a separate LFS server.
How LFS Works
You track a file pattern:
git lfs track "*.psd"When you commit a matching file, Git LFS intercepts it and stores the binary data on the LFS server
What gets committed to Git history is a small pointer file (~130 bytes) — not the binary data
When you checkout or pull, LFS automatically downloads the actual file from the LFS server
Your working directory always has the real file — the pointer is only visible in the object store
Installing Git LFS
Install and initialize LFS
# Install via package manager brew install git-lfs # macOS sudo apt install git-lfs # Ubuntu/Debian winget install git-lfs # Windows # Initialize LFS (installs hooks into ~/.gitconfig) git lfs install
Example output from git lfs install
Updated Git hooks. Git LFS initialized.
Tracking File Patterns
The git lfs track command adds patterns to a .gitattributes file. Always commit .gitattributes to the repository so that all collaborators use LFS for the same files.
Track large file patterns
# Track design files git lfs track "*.psd" git lfs track "*.ai" git lfs track "*.sketch" # Track video files git lfs track "*.mp4" git lfs track "*.mov" # Track compiled binaries git lfs track "*.exe" git lfs track "dist/**" # Commit .gitattributes (IMPORTANT!) git add .gitattributes git commit -m "chore: configure Git LFS tracking"
.gitattributes after tracking
*.psd filter=lfs diff=lfs merge=lfs -text *.ai filter=lfs diff=lfs merge=lfs -text *.mp4 filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text
What a Pointer File Looks Like
When an LFS-tracked file is committed, this is what actually goes into Git history. The oid is a SHA-256 hash of the actual file content, and size is the file size in bytes. The LFS server uses the OID to store and retrieve the actual binary data.
LFS pointer file content (what goes in Git history)
version https://git-lfs.github.com/spec/v1 oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393 size 2097152
Inspecting LFS Files
Inspect LFS-tracked files
# List all files currently tracked by LFS git lfs ls-files # Show LFS status (modified, staged, etc.) git lfs status # Show which patterns are being tracked git lfs track
git lfs ls-files output
4d7a214614 * assets/hero-image.psd 8c3e1f2a90 * videos/demo-reel.mp4 b1a9d7f3c5 * design/mockups.ai
Fetching and Pulling LFS Objects
LFS fetch and pull commands
# Download LFS objects for the current branch (but don't checkout) git lfs fetch # Download LFS objects for all branches and tags git lfs fetch --all # Download AND checkout (most common — equivalent to git pull) git lfs pull # If you cloned without LFS, retrieve LFS objects after the fact git lfs pull
Pushing LFS Objects
Push LFS objects to remote
# Normal push — LFS objects are pushed automatically along with commits git push origin main # Force push all LFS objects (useful if objects are missing on server) git lfs push --all origin
Regular Git vs Git LFS
Dimension | Regular Git | Git LFS |
|---|---|---|
Binary file storage | Full copy of every version in history | Pointer in history, binary on LFS server |
Clone size | Entire history including all binary versions | Only current version of LFS files by default |
Diff/merge for binaries | No meaningful diff; merges fail | Same limitation — binary files still need manual merge |
Server requirements | Any Git host | Requires LFS-capable server (GitHub, GitLab, Bitbucket support it) |
Working directory | Same as regular Git | Real files downloaded transparently on checkout |
Cost | No extra cost | GitHub: 1GB free storage + 1GB bandwidth/month; paid plans available |
Offline work | Full history always available locally | LFS objects may not be available offline without pre-fetching |
Migrating Existing Binary Files to LFS
If your repository already has a history of large binary files committed the regular way, git lfs migrate can rewrite that history to use LFS. This rewrites commits — everyone must re-clone after migration.
Migrate existing binary files to LFS
# Migrate all .mp4 files across entire history git lfs migrate import --include="*.mp4" --everything # Migrate multiple patterns git lfs migrate import --include="*.psd,*.ai,*.mp4,*.mov" --everything # After migration, force push to update remote git push --force --all git push --force --tags
GitHub LFS Storage and Bandwidth
Free: 1 GB storage + 1 GB bandwidth per month
Data packs: $5/month for 50 GB storage + 50 GB bandwidth
Bandwidth: counts each time an LFS object is downloaded (clone, pull, etc.)
Storage: counts the total size of all LFS objects stored
If you exceed limits, pushes and clones of LFS objects will fail until you upgrade