Einführung
Gits inhaltsadressiertes Modell dedupliziert identische Objekte, aber ähnliche-aber-nicht-identische Blobs (eine Datei und ihre spätere Bearbeitung) würden weiterhin volle Größe kosten. Pack-Dateien lösen das mit Delta-Kompression: Speichern eines Objekts als Basis plus eine Sequenz von "copy/insert"-Anweisungen, die ein anderes Objekt erzeugen.
Wie es funktioniert
Beim Packen vergleicht Git Kandidatenobjekte (üblicherweise Blobs ähnlicher Größe und Typs) und wählt eine Basis für jedes. Das Delta zeichnet auf:
- Das Basisobjekt (per Offset im Pack oder per SHA).
- Größen von Basis und Ziel.
- Einen Strom von copy- (Bytes aus der Basis verwenden) und insert-Anweisungen (literale Bytes).
Der Delta-Strom wird dann zlib-komprimiert.
Deltas inspizieren
git verify-pack -v .git/objects/pack/pack-<hash>.idx | head -20
Die Ausgabe enthält Spalten für gepackte Größe, Tiefe und Basis-SHA für Delta-Einträge. Ein Nicht-Delta-Objekt hat keine Basis.
Einstellbare Werte
git config pack.window 250 # pro Objekt geprüfte Kandidaten
git config pack.depth 50 # maximale Kettenlänge
git config pack.threads 4
git config pack.windowMemory 100m
git config pack.deltaCacheSize 256m
pack.window steuert, wie aggressiv Git nach guten Basen sucht; pack.depth begrenzt, wie lang Delta-Ketten wachsen können.
Aggressives vs. normales Repack
git repack -a -d # Standard
git repack -a -d --depth=250 --window=250 # aggressiv
git gc --aggressive
Aggressive Repacks werfen vorhandene Deltas weg und berechnen sie von Grund auf neu, kosten CPU, verbessern aber möglicherweise das Ergebnis.
Erreichbarkeit und Bitmaps
Server kombinieren Delta-Packing oft mit Erreichbarkeits-Bitmaps für schnelle Clone-Set-Berechnung:
git repack -adb
Warum manche Objekte nicht deltifiziert werden
- Kryptografisch zufällige Daten (bereits inkomprimierbar).
- Objekte, deren einzige Kandidaten kleiner sind als der konfigurierte Schwellenwert.
- Objekte außerhalb der Reichweite des Pack-Fensters.
Pack-übergreifende Deltas
Multi-Pack-Indexe (git multi-pack-index) plus der Repack-Modus --geometric (Git 2.33+) machen es möglich, Deltas effizient über viele Packs hinweg zu pflegen:
git repack --geometric=2 -d
Dies ist das empfohlene Schema für große Repositories.
Delta-Inseln
Für Server-Betreiber, die viele Forks desselben Projekts hosten, partitionieren Delta-Inseln Objekte so, dass Deltas nur innerhalb der Ref-Menge eines Forks geschehen, was schnelles Klonen jedes einzelnen Forks ermöglicht:
git config pack.island "refs/remotes/(.*)/heads"
git config pack.islandCore "main"
git repack -adb
Ohne Inseln könnte ein Objekt, das nur aus Fork A erreichbar ist, gegen ein Objekt deltifiziert werden, das nur aus Fork B erreichbar ist; Forks A's Klon zu bedienen würde dann erfordern, auch Objekte aus B zu senden. Die meisten Nutzer brauchen das nie, aber es ist die Geheimzutat hinter der Pack-Server-Performance von GitHub, GitLab und Bitbucket.
Häufige Fehler
Annehmen, Deltas seien vorwärts (neuer = Basis) oder rückwärts (älter = Basis); Git ist agnostisch und wählt das, was ein kleineres Delta ergibt. Packs zwischen Repositories mit verschiedenen SHA-Präfixen kopieren; die Daten sind inhaltsadressiert, also funktioniert das, aber das Umbenennen von Dateien basierend auf Teil-Hashes kann brechen. Absurd hohe pack.depth-Werte verfolgen, um kleine Packs zu erreichen; tiefere Ketten bedeuten langsameren Objektzugriff (jedes Delta in der Kette muss angewendet werden). Schließlich: Erwarten, dass Delta-Kompression Speicher bei bereits komprimierten Binärdateien (Videos, JPGs) spart; tut sie fast nie. Verwenden Sie dafür Git LFS.