What you will achieve
You will install Git LFS, configure it for a project that includes images, video, and design files, migrate any existing binaries already in history, and verify everything works for collaborators.
Step 1: install Git LFS
# macOS
brew install git-lfs
# Debian/Ubuntu
sudo apt install git-lfs
# Verify
git lfs version
Activate per user (run once):
git lfs install
Step 2: enter the repo
cd path/to/repo
git lfs install --local # ensure hooks for this repo
Step 3: track file types
git lfs track "*.psd"
git lfs track "*.ai"
git lfs track "*.sketch"
git lfs track "*.mp4"
git lfs track "*.mov"
git lfs track "*.wav"
git lfs track "assets/raw/**"
Each command appends a pattern to .gitattributes:
*.psd filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
assets/raw/** filter=lfs diff=lfs merge=lfs -text
Step 4: commit .gitattributes
git add .gitattributes
git commit -m "Configure LFS tracking for media files"
This is essential - other clones must see .gitattributes to know LFS applies.
Step 5: add new media files
cp ~/Downloads/logo.psd assets/
git add assets/logo.psd
git commit -m "Add logo PSD"
git push
Verify it went via LFS:
git lfs ls-files
# 1234abcd * assets/logo.psd
Step 6: migrate existing binaries
If the repo already has binaries committed normally, rewrite history:
git lfs migrate import --include="*.psd,*.mp4,*.mov,*.wav" --everything
This converts every matching binary in every commit into an LFS pointer. The repo's .git directory shrinks dramatically.
SHAs change. Force-push and have collaborators reclone:
git push --force --all
git push --force --tags
Step 7: lock binary files (optional)
Mark binaries as lockable to prevent concurrent edits:
# .gitattributes
*.psd filter=lfs diff=lfs merge=lfs -text lockable
*.ai filter=lfs diff=lfs merge=lfs -text lockable
Working with locks:
git lfs lock assets/logo.psd
# edit...
git push # release lock on push
# or
git lfs unlock assets/logo.psd
git lfs locks # see who has what locked
Step 8: verify on a fresh clone
cd /tmp
git clone <url> test-clone
cd test-clone
ls -la assets/logo.psd # should be the actual file, not a pointer
Inspect a pointer file:
git cat-file -p HEAD:assets/logo.psd
# version https://git-lfs.github.com/spec/v1
# oid sha256:8a1b...
# size 12345678
Step 9: handle quotas
GitHub's free LFS tier is 1 GB storage and 1 GB bandwidth per month. Past that, you pay or self-host.
Self-hosted options:
- Gitea or Forgejo - LFS built in.
- GitLab - LFS native.
- Bare LFS server (rudolfs, lfs-test-server) behind your own object store.
Step 10: pruning
git lfs prune # remove old local LFS files no longer referenced
git lfs prune --dry-run # preview
Useful for laptops with limited disk; the remote keeps everything.
Performance tips
- Skip LFS smudge on initial clone for speed:
GIT_LFS_SKIP_SMUDGE=1 git clone <url>; latergit lfs pull. - Use
git lfs fetch --recentin CI to limit downloaded LFS data. - Configure CI caches to retain
.git/lfs/.
Troubleshooting
- Binary committed before
.gitattributeswas set up: still a regular blob; usegit lfs migrate. - Pointer file showing in working tree: LFS not installed locally, or smudge skipped. Run
git lfs installandgit lfs pull. - Push rejected for "exceeds maximum size": LFS server quota reached.
The result
The repo's .git stays small; binaries live on the LFS server and are fetched on demand. Designers and engineers collaborate without bloating clone times. The setup pays back the first time someone joins the team and clones in seconds instead of an hour.