lfs-size-track: Git LFS nach Dateigröße

Ein Pre-Commit-Hook, der jede gestagte Datei über einer Größenschwelle mit dem normalen git-lfs-Client in Git LFS verschiebt.

Das Problem

Git LFS entscheidet nach Pfadmuster, welche Dateien es speichert, zum Beispiel git lfs track "*.psd". Große Dateien mit einer gängigen Endung (.json, .bin, .bam), Dateien ohne Endung und Dateien an unerwarteten Orten landen in der Git-Historie, bevor es jemand bemerkt. Danach entfernt sie nur noch ein Umschreiben der Historie.

git-lfs/git-lfs#282, "support tracking by file size", ist seit 2015 offen und hatte am 2026-10-21 176 Reaktionen und 135 Kommentare. Damit ist es das meistdiskutierte offene Issue des Projekts. Typische Fälle im Thread sind .bam-Dateien aus der Genomik, mitgelieferte Binärdateien ohne Endung und Spiele-Assets, die nach dem ersten Commit über eine Grenze wachsen.

Die Maintainer halten Git selbst für den richtigen Ort für einen Größenfilter. Ein Pull Request, der --min-size hinzufügt, wartet seit Juni 2026 auf ein Review und würde den neuen Client auf jedem Rechner voraussetzen. Der vorhandene Hook lfs-auto-track läuft nur über npm, husky und lint-staged, und ein Kommentar im Issue fragt nach einer Version außerhalb des npm-Ökosystems.

Wie es funktioniert

lfs-size-track läuft als Pre-Commit-Hook und prüft jede gestagte Datei. Eine Datei über der Schwelle, die LFS noch nicht erfasst, wird mit git lfs track --filename über ihren genauen Pfad getrackt und erneut als LFS-Pointer gestagt. Der Commit enthält dann den Pointer und den Eintrag in .gitattributes. Kleinere Dateien bleiben unberührt. Es nutzt den normalen git-lfs-Client, kein Rechner braucht eine gepatchte Version.

Die Schwelle liegt standardmäßig bei 10 MB. Sie lässt sich mit git config lfs-size-track.threshold oder --threshold in dezimalen oder binären Einheiten setzen (500KB, 10MB, 1GiB), und --exclude überspringt Pfade, die auf ein Glob-Muster passen. Installiert wird der Hook entweder mit lfs-size-track install als .git/hooks/pre-commit oder über das pre-commit-Framework.

Für Repositories, die schon große Dateien enthalten, listet scan die Dateien in HEAD über der Schwelle, die in Git statt in LFS liegen. scan --history tut dasselbe für jeden Commit. Beide geben den git lfs migrate-Befehl aus, der sie verschieben würde.

Das ist die Ausgabe in einem Test-Repository mit einer gestagten .bam-Datei von 30 MB und einer von 2 KB bei der Standardschwelle:

$ 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

Im selben Repository wurde danach eine Datei von 15 MB ohne LFS committet und im nächsten Commit gelöscht. scan meldet für HEAD nichts, scan --history findet sie:

$ 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"

Tests

Die Tests legen in einem temporären Verzeichnis echte Git-Repositories mit git lfs install --local an, schreiben Dateien aus Zufallsbytes und prüfen mit git cat-file, ob der gespeicherte Blob ein LFS-Pointer ist. Eine Datei namens data/sample [1].bam über der Schwelle muss zum Pointer werden, während eine kleine Datei mit derselben Endung in Git bleibt, und die geänderte .gitattributes muss gestagt sein. Weitere Tests decken Dateien ab, die schon ein LFS-Muster erfasst, ausgeschlossene Pfade, eine Datei mit ungestagten Änderungen (Exit-Code 1, nicht neu gestagt), die Schwelle aus git config, scan auf HEAD und mit --history, den installierten Hook bei einem echten git commit, die Weigerung, einen fremden Hook zu überschreiben, und den Parser für Größenangaben. Das CI führt die Tests auf Python 3.10 bis 3.13 mit dem git-lfs-Client des Runners aus, prüft mit ruff und scannt das eigene Repository mit einer Schwelle von 1 MB.

Grenzen

  • Dateien werden über ihren genauen Pfad getrackt. Wird eine solche Datei später umbenannt, braucht es einen neuen Commit über den Hook oder git lfs track von Hand.
  • Hat eine große Datei zusätzlich ungestagte Änderungen, stagt der Hook sie nicht neu, weil das die ungestagten Änderungen in den Commit aufnehmen würde. Er endet mit Exit-Code 1 und bittet darum, die Datei vollständig zu stagen.
  • Eine Datei, die unter die Schwelle schrumpft, bleibt in LFS. Dateien wieder aus LFS herauszunehmen, bleibt dem Nutzer überlassen.
  • Jeder Klon braucht git lfs install, wie bei jedem LFS-Repository.

Code und Installationsanleitung liegen unter github.com/Jwrede/lfs-size-track.