lfs-size-track: Git LFS tracking by file size

A pre-commit hook that moves every staged file above a size threshold into Git LFS, using the stock git-lfs client.

The problem

Git LFS decides by path pattern which files it stores, for example git lfs track "*.psd". Large files with a common extension (.json, .bin, .bam), files without an extension, and files in unexpected places end up in the Git history before anyone notices. Afterwards only a history rewrite removes them.

git-lfs/git-lfs#282, "support tracking by file size", has been open since 2015 and had 176 reactions and 135 comments as of 2026-10-21, which makes it the most discussed open issue in the project. Typical cases in the thread are genomics .bam files, vendored binaries without an extension, and game assets that grow past a limit after their first commit.

The maintainers suggest that Git itself would be the right place for a size filter. A pull request that adds --min-size has been waiting for review since June 2026, and it would require the new client on every machine. The existing hook lfs-auto-track runs only through npm, husky and lint-staged, and a commenter in the issue asked for a version outside the npm ecosystem.

How it works

lfs-size-track runs as a pre-commit hook and checks every staged file. A file above the threshold that LFS does not already handle is tracked by its exact path with git lfs track --filename and staged again as an LFS pointer, so the commit contains the pointer and the .gitattributes entry. Smaller files are not touched. It uses the stock git-lfs client, so no machine needs a patched version.

The threshold defaults to 10 MB. It can be set with git config lfs-size-track.threshold or --threshold in decimal or binary units (500KB, 10MB, 1GiB), and --exclude skips paths that match a glob. The hook is installed either with lfs-size-track install as .git/hooks/pre-commit or through the pre-commit framework.

For repositories that already contain large files, scan lists files in HEAD above the threshold that are stored in Git rather than LFS, and scan --history does the same for every commit. Both print the git lfs migrate command that would move them.

This is the output in a test repository with a 30 MB and a 2 KB .bam file staged, at the default threshold:

$ lfs-size-track hook
lfs-size-track: data/reads.bam (30.0 MB) is now tracked by Git LFS
$ cat .gitattributes
data/reads.bam filter=lfs diff=lfs merge=lfs -text

In the same repository a 15 MB file was then committed without LFS and deleted in the next commit. scan reports nothing for HEAD, and scan --history finds it:

$ lfs-size-track scan
No files above 10.0 MB outside Git LFS in HEAD.
$ lfs-size-track scan --history
Files above 10.0 MB stored in Git instead of LFS (in the history):
    15.0 MB  old.iso

To rewrite history (changes commit IDs):
  git lfs migrate import --everything --include="old.iso"

Testing

The tests create real Git repositories with git lfs install --local in a temporary directory, write files of random bytes and check with git cat-file whether the stored blob is an LFS pointer. A file named data/sample [1].bam above the threshold must become a pointer while a small file with the same extension stays in Git, and the changed .gitattributes must be staged. Further tests cover files already matched by an LFS pattern, excluded paths, a file with unstaged changes (exit code 1, not restaged), the threshold from git config, scan on HEAD and with --history, the installed hook running during a real git commit, the refusal to overwrite a foreign hook, and the size parser. CI runs the tests on Python 3.10 to 3.13 with the git-lfs client of the runner, runs ruff, and scans the tool's own repository with a 1 MB threshold.

Limitations

  • Files are tracked by exact path. Renaming such a file later needs a new commit through the hook, or git lfs track by hand.
  • If a large file also has unstaged changes, the hook does not restage it, because that would add the unstaged changes to the commit. It exits with code 1 and asks for the file to be staged fully.
  • A file that shrinks below the threshold stays in LFS. Moving files back out of LFS is left to the user.
  • Each clone needs git lfs install, as with any LFS repository.

The code and installation instructions are at github.com/Jwrede/lfs-size-track.