Git Internals

Hi zusammen,

primär für mich zur Dokumentation und zum besseren Verständnis versuche ich in diesem Artikel ein wenig die GIT-Internals zu beleuchten. Ich werde den Artikel bei Bedarf nach und nach ergänzen, damit ich immer weiß, wo ich meine Referenz finden kann.

Bitte beachtet auch, dass sich der Artikel aktuell in einer sehr frühen Phase befindet, es können also durchaus noch einige Fehler und Unvollständigkeiten vorhanden sein.

Ein interessanter Aspekt, der mir bisher auch mehr oder weniger unbekannt war sind die das Blob-Format und die GIT-Packfiles.

Zunächst interessiert uns hier, wie GIT überhaupt die Objekte speichert (BLOB). Ich habe eine Weile gegoogelt, bis ich zu dem Ergebnis gekommen bin, dass es sich hier lediglich um auf eine bestimmte Artgezippte Files handelt.

GIT verwendet Pack-Files, um einerseits die Dateigrösse des Repositories zu minimieren und andererseits, um einen effizienten Zugriff auch auf ältere BLOBs zu ermöglichen. Zusätzlich bieten Pack files GIT die Möglichkeit, statt jeweils kompletter Objekte nur die jeweiligen Deltas zu speichern.

Stellt man sich vor, man hat eine Datei mit 1500 Zeilen und ändert nur eine Zeile würde GIT per Default ein zweites Blob Objekt anlegen und so die Daten redundant ablegen. Das Ganze würde also auf Dauer sehr groß und ineffizient werden.

Hierfür bietet GIT das Format der Packfiles, die Packfiles dienen quasi u.a. dazu, die Deltas des zweiten, um eine Zeile veränderten Objektes zu speichern. Git würde also wirklich nur die veränderte Zeile speichern und hierzu eine Referenz auf das erste File speichern.

Siehe hierzu noch Kommentar weiter unten.

Wo findet man nun diese Pack-Files? In einem frisch erzeugten Repository erstmal gar nicht. Git scannt periodisch das Repository und erzeugt die entsprechenden Pack-Files. Damit wir was haben womit wir arbeiten können kann man GIT auch zwingen ein bestehendes Repository zu packen mit


git gc

Interessant sind auch

git repack

und

git unpack-objects

Mit diesen Befehlen kann das Repository erneut packen und so auf eventuelle Änderungen reagieren. Man kann so auch die bereits gepackten Objekte wieder entpacken.

Dann schaut man direkt mal in


repo-path/.git/objects/packs

Hier sollte man jetzt mindestens 2 Files vorfinden, und zwar ein xxx.idx und ein xxx.pack. Das idx-File ist ein Index in das Packfile, das einen schnellen Zugriff ermöglicht. Das Packfile selbst beinhaltet die entsprechenden Inhalte.

Ein weiterhin interessante Info ist, dass diejenigen Objekte, die nun in dem Packfile gelandet sind physikalisch aus dem Objekt-Speicher des GIT-Repositories gelöscht worden sind, was man direkt sehen kann, wenn man in


repo-path/.git/objects

nachschaut.

Primär betrachte ich hier das Packfile Format selbst:

Zunächst befindet sich in einem Packfile das Wort „PACK“ (die ersten 4 Byte = 50 41 40 4B), dies dient lediglich dazu, den richtigen Anfang zu finden.

Die nächsten 4Byte sind die Versionsnummer des Packfiles (die wird in git ab 1.5.4 typischerweise 2 = 00 00 00 02 sein, siehe Referenz am Ende des Artikels für weitere Infos)

Die nächsten 4Byte geben die Anzahl der Einträge im Packfile an (in meinem Beispiel wäre das 64 = 00 00 00 3F)

Nachfolgend sind die einzelnen Objekte aufgelistet (jeweils HEADER + CONTENT) in einem speziellen Format. Jedes Byte im HEADER besteht tatsächlich aus 7 Bit (das erste Bit jedes Bytes gibt an, ob nachfolgend bereits der Content anfängt (=0), oder ob weitere Header-Informationen folgen (=1).

Das erste Byte in meinem Beispiel ist folgendes:


1 001 0111

Das erste Bit (1) gibt an, dass weitere HEADER-Informationen folgen.

Die folgenden 3 Bit (001) geben an, was der folgende Content für einen Typ hat (001 = Commit, siehe hierzu die exzellente Referenz weiter unten)

Die letzten 4Bit des ersten Byte und alle darauffolgenden bis zum Content (bis das erste Bit 0 ist) geben die Größe des Contents an (in entpackter Form).


1 001 0111 0001 0000

Schliessen wir die Kennungsbits aus, ergibt sich folgender Content:


001 0111 001 0000

Nehmen wir den Typ noch weg, ergibt sich folgender Inhalt:


0111 001 0000

Die Größe wird gelesen, in dem die 4 Bits des ersten Byte an das darauffolgende Byte angehängt werden:


001 0000 0111

Das bedeutet, das nachfolgende Commit-Objekt hat eine Größe von 263 Bytes, wenn es entpackt wird. Gepackt sind die Objekte im ZLIB-Algorithmus.

Interessant hierbei sind noch die beiden Typen


110 - Objekt Referenz

und


111 - Objekt Delta

Diese beiden Typen werden für die weiter oben bereits angesprochende Speicherung von Delta-Informationen verwendet.
Objekt-Delta ist der 20Byte-SHA1 Hashcode des Objektes, das referenziert wird (und das sich übrigens im gleichen Packfile befinden muss).

Referenzen:

Packfiles

Blobs


War dieser Blogeintrag für Sie interessant? Evtl. kann ich noch mehr für Sie tun.

Trainings & Know-How aus der Praxis zu

  • Apache Wicket 1.4.x, 1.5.x, 1.6.x
  • GIT – Best Practices, Einsatz, Methoden
  • Spring
  • Java
  • Scrum & Kanban
  • Agiles Arbeiten
Consulting & Softwareentwicklung

  • Requirements Engineering
  • Qualitätssicherung
  • Software-Entwicklung
  • Architektur
  • Scrum & Kanban
Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s