What is Git LFS?
Git is optimized for tracking changes in text source code. Commit large files—images, video, design originals, datasets, binaries—as-is, and every version piles up in the .git history. The repository balloons to several GB, and clone and fetch become extremely slow. Once a large file is committed, it stays in history forever even if you delete it later.
Git LFS (Large File Storage) is an extension that solves this. The real content of large files lives in a separate LFS store; the Git repository only commits a pointer file (a few dozen bytes of text) that points to that content.
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345678On checkout, Git reads the pointer and downloads the real file from the LFS server. History stays light, and you only fetch the versions you need.
Installation
# macOS
brew install git-lfs
# Debian / Ubuntu
sudo apt install git-lfs
# RHEL / Rocky
sudo dnf install git-lfs
# check
git lfs versionAfter installing, initialize LFS once per user account (once per machine).
git lfs installThis command registers LFS filter/smudge/clean hooks in your global Git config.
Specifying files to track — git lfs track
Inside the repository, you specify which files LFS should handle using patterns.
#
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "*.zip"
#
git lfs track "assets/models/**"
# check
git lfs trackThis command creates or updates a .gitattributes file at the repository root.
*.psd filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -textYou must commit .gitattributes. Without it, collaborators and CI treat those files as ordinary Git files and LFS will not work.
git add .gitattributes
git commit -m "chore: track binary assets with Git LFS"After that, adding, committing, and pushing files like *.psd is handled by LFS automatically.
git add design/cover.psd
git commit -m "feat: add cover artwork"
git push origin mainChecking tracking status
# LFS file list
git lfs ls-files
#
# 4d7a214614 * design/cover.psd
# a91f0c8e2b - assets/intro.mp4
# save·LFS
git lfs env
# file file check
git lfs pointer --file=design/cover.psdIn ls-files, * means the real file is present in the working tree; - means only the pointer is present.
Migrating existing history
If large files were already committed the ordinary way, git lfs track alone will not clean up past commits. Rewrite history with git lfs migrate.
# *.psd, *.zip LFS
git lfs migrate import --include="*.psd,*.zip"
# /
git lfs migrate import --include="*.psd" --everythingmigrate import rewrites every commit hash (history rewrite). If the branch is shared, agree with collaborators first, and push with git push --force-with-lease. Always back up before you start.
To reverse LFS and convert files back to ordinary Git objects, use git lfs migrate export --include="*.psd".
Partial checkout — saving bandwidth
You can avoid downloading every LFS file in a large repository.
# LFS file clone
GIT_LFS_SKIP_SMUDGE=1 git clone <repo-url>
#
git lfs pull --include="design/*.psd"
#
git config lfs.fetchexclude "assets/raw/**"
# file
git config lfs.fetchrecentcommitsdays 7LFS vs ordinary Git
| Item | Ordinary Git commit | Git LFS |
|---|---|---|
| What goes into the repo | Entire file (every version) | Pointer text only |
| Clone speed | Slows as history grows | Fast — only needed versions |
| Diff of large files | Almost impossible | Pointer comparison (metadata) |
| History size | Keeps growing | Stays light |
| Best for | Text source | Images, video, binaries, datasets |
Repository size and cleanup
LFS objects are cached locally in .git/lfs/objects. You can prune old objects.
# LFS
git lfs prune
#
git lfs prune --dry-run
# LFS
git lfs fetch --allHosting services such as GitHub and GitLab have separate quotas for LFS storage and bandwidth (e.g. GitHub Free: 1 GB storage / 1 GB bandwidth per month). Check costs in advance for large datasets.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| After clone, files appear as pointer text | git lfs install was not run → run it, then git lfs pull |
| LFS does not work for collaborators | .gitattributes not committed → commit and share it |
batch response: 403 on push | LFS permission or quota issue. Check token and capacity |
| Large files already committed as ordinary Git objects | Convert with git lfs migrate import |
smudge filter lfs failed | Network/auth problem. Bypass with GIT_LFS_SKIP_SMUDGE=1, then git lfs pull |
.gitattributes best practices
#
*.psd filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
# archive /
*.zip filter=lfs diff=lfs merge=lfs -text
*.tar.gz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -textThe -text attribute treats the file as binary and prevents line-ending conversion (CRLF↔LF). Always attach it to LFS-tracked files.
Summary
| Task | Command |
|---|---|
| Machine init (once) | git lfs install |
| Set tracking pattern | git lfs track "*.psd" |
| List tracked files | git lfs ls-files |
| Convert existing history | git lfs migrate import --include="*.zip" |
| Clone pointers only | GIT_LFS_SKIP_SMUDGE=1 git clone |
| Fetch only what you need | git lfs pull --include="..." |
| Prune local cache | git lfs prune |
Git LFS fills Git’s weakness with text-oriented storage by treating large files as lightweight pointers. The essentials: always commit .gitattributes, migrate existing large files, and watch your hosting quotas.
AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.