Einführung
Gits Repository ist im Kern ein inhaltsadressierter Objektspeicher. Es gibt genau vier Objekttypen: Blob, Tree, Commit und Tag. Jede Operation berührt sie letztlich. Diese vier Typen zu verstehen entmystifiziert das meiste an Git.
Blobs
Ein Blob speichert Dateiinhalte, mehr nicht. Kein Dateiname, keine Berechtigungen, keine Historie. Zwei Dateien mit identischem Inhalt teilen sich einen Blob, unabhängig davon, wo sie im Tree liegen.
echo "hello" | git hash-object --stdin
# ce013625030ba8dba906f756967f9e9ca394464a
Trees
Ein Tree ist ein Verzeichniseintrag: eine sortierte Menge von (mode, type, sha, name)-Einträgen. Unterverzeichnisse sind selbst Trees, rekursiv. Jeder Commit zeigt auf genau einen Wurzel-Tree.
git ls-tree HEAD
# 100644 blob a1b2... README.md
# 040000 tree c3d4... src
Commits
Ein Commit-Objekt enthält:
- Einen Zeiger auf einen Wurzel-Tree.
- Null oder mehr Eltern-Commits (null für die Wurzel, zwei oder mehr für Merges).
- Autor und Committer mit Zeitstempeln.
- Eine Nachricht.
- Optionale GPG/SSH-Signatur.
git cat-file -p HEAD
# tree 9f1a...
# parent b2c3...
# author Ada <[email protected]> 1714300000 +0000
# committer Ada <[email protected]> 1714300000 +0000
#
# Add greeting
Tags
Ein annotated Tag ist ein eigenes Objekt, das auf ein anderes Objekt zeigt (fast immer einen Commit), mit Tagger-Info und einer Nachricht. Lightweight-Tags sind nur Refs und haben kein Objekt.
git cat-file -t v1.0.0
# tag
git cat-file -p v1.0.0
Zusammensetzen
Den Tree eines Commits manuell durchlaufen:
git cat-file -p HEAD^{tree}
git cat-file -p HEAD^{tree}:src
git cat-file -p HEAD:README.md
Die ^{tree}-Peel- und die commit:path-Syntax sind die Art, wie alle Git-Werkzeuge navigieren.
Speicherung
Jedes Objekt ist zlib-komprimiert und adressiert durch den SHA-1 (oder SHA-256) seines unkomprimierten Inhalts plus Header. Identischer Inhalt, irgendwo in der Historie, dedupliziert sich automatisch.
Tree-Eintragsmodi
Das mode-Feld in einem Tree-Eintrag ist eine kleine Menge POSIX-ähnlicher Dateimodi:
100644: reguläre nicht-ausführbare Datei (Blob).100755: ausführbare Datei (Blob).120000: symbolischer Link (Blob, dessen Inhalt das Ziel ist).040000: Unterverzeichnis (Tree).160000: Gitlink (Submodule-Referenz auf einen Commit-SHA).
git ls-tree HEAD
git update-index --chmod=+x scripts/run.sh
git ls-tree HEAD scripts/
Git ist absichtlich begrenzt; willkürliche Berechtigungen und Eigentümerschaften werden nicht gespeichert.
Häufige Fehler
Glauben, dass Git Diffs speichert. Tut es nicht; es speichert vollständige Snapshots, dedupliziert nach Hash und später Delta-komprimiert in Pack-Dateien. Trees mit Verzeichnissen auf der Festplatte verwechseln; Trees sind unveränderliche Objekte. Lightweight-Tags mit annotated verwechseln; nur annotated Tags tragen Metadaten und Signaturen. Schließlich: Erwarten, dass das Umbenennen einer Datei den Blob ändert; der Blob ist derselbe, nur der Name-Eintrag des Trees ändert sich. Verbringen Sie zehn Minuten mit git cat-file -p auf einem echten Repository und das Modell wird zur zweiten Natur.