🧬Git intern
Git ist im Kern ein inhaltsadressierter Schlüssel-Wert-Speicher: Du gibst Git Daten, Git gibt dir einen Schlüssel – den Hash dieser Daten. Darauf bauen vier Objekttypen auf, und darüber liegen Referenzen wie Branches und HEAD.
🧱Vier Objekttypen
Inhalt einer Datei – ohne Namen, ohne Rechte. Zwei gleiche Dateien = ein Blob.
Ein Verzeichnis: Liste aus Modus, Name und ID von Blobs bzw. Unter-Trees.
Schnappschuss: Wurzel-Tree + Eltern-Commits + Autor/Committer + Nachricht.
Annotiertes Tag: zeigt auf ein Objekt (meist Commit), mit Tagger, Datum, Nachricht.
HEAD ─▶ refs/heads/main ─▶ commit 4ac0b51 ──parent──▶ commit ab61007
refs/tags/v1.0.0 ─▶ tag e1d0779 ─▶ commit 4ac0b51
commit 4ac0b51 ─▶ tree c8e6bcf commit ab61007 ─▶ tree 3b8dd6b
.gitignore ─▶ blob c2658d7 ◀──── gleich ──── .gitignore
README.md ─▶ blob (Version 2, neu) README.md ─▶ blob 0805455
src-a.txt ─▶ blob 587be6b ◀──── gleich ──── src-a.txt
src/ ─▶ tree d60698e ◀──── gleich ──── src/
└─ app.js ─▶ blob 226344d🔑Inhalt → SHA-1: git hash-object im Browser
6 Bytes Inhalt (6 Zeichen – Umlaute brauchen in UTF-8 zwei Bytes) + 7 Bytes Header. Dateiname und Rechte stehen nicht im Blob – die speichert der Tree.
13 → 21 Bytes. Die ersten zwei Bytes 78 9c sind der zlib-Header (Standardstufe). Bei sehr kleinen Objekten wird die Datei durch Header und Prüfsumme sogar größer. Andere zlib-Implementierungen können andere, aber gleichwertige Bytes erzeugen – die ID hängt nur vom unkomprimierten Inhalt ab.
git init --object-format=sha256 – 64 statt 40 Hex-Zeichen. Laut Git-Doku gibt es derzeit keine Interoperabilität zwischen SHA-1- und SHA-256-Repositories; Standard bleibt sha1.
--object-format=sha256); der Standard ist laut Git-Dokumentation weiterhin sha1.🔍Objekt-Explorer
Ändere ein Zeichen: neuer Blob → neuer Tree → neuer Commit → neues Tag. Alles, was gleich bleibt, wird wiederverwendet.
tree c8e6bcf4426f27ebb16bd645e01bcb1af203fddc parent ab610079079d544e6843bd481b91698ba4540803 author Alice <alice@example.org> 1767225660 +0100 committer Alice <alice@example.org> 1767225660 +0100 README erweitert
- Ein Commit zeigt auf genau einen Wurzel-Tree (vollständiger Schnappschuss, kein Diff!), auf seine Eltern, und enthält Autor, Committer (mit Unix-Zeit und Zeitzone) und Nachricht.
- Hell hervorgehoben: alles, was von diesem Objekt aus erreichbar ist.
- Die IDs stimmen mit echtem Git überein:
GIT_AUTHOR_DATE="1767225600 +0100"usw. mitgit commit-treenachgerechnet (Tests).
📂Das .git-Verzeichnis
HEAD – wo stehe ich?
Eine Textdatei. Normalerweise eine symbolische Referenz auf den aktuellen Branch. Im „detached HEAD“-Zustand steht hier direkt eine Commit-ID.
ref: refs/heads/main
👉Referenzen und HEAD
HEAD ist eine symbolische Referenz auf main. Ein Commit schreibt die neue ID in refs/heads/main – HEAD selbst bleibt unverändert und „wandert“ mit.
📋Der Index
Der Index (.git/index) ist eine flache, sortierte Liste aller Dateien des nächsten Commits – nicht nur der geänderten. git add schreibt den Blob in die Objekt-Datenbank und trägt seine ID im Index ein. git commit baut aus dem Index die Trees und den Commit.
Die Spalte „Stage“ ist normalerweise 0. Während eines Merge-Konflikts stehen für eine Datei bis zu drei Einträge im Index: 1 = gemeinsame Basis, 2 = „ours“ (HEAD), 3 = „theirs“ (der hereinkommende Branch). Erst git add ersetzt sie durch einen Eintrag mit Stage 0.
$ git ls-files --stage # normal 100644 c2658d7d1b31848c3b71960543cb0368e56cd4c7 0 .gitignore 100644 0805455a24b6c68fbc38d0fa5d121f735984285d 0 README.md $ git ls-files --stage # während eines Konflikts 100644 <id der Basis> 1 config.txt 100644 <id von HEAD> 2 config.txt 100644 <id von theirs> 3 config.txt $ git show :1:config.txt # Basis-Fassung ansehen $ git checkout --theirs config.txt
📦Lose Objekte und Packfiles
Vorher: jede Version einzeln (zlib) Nachher: git gc
.git/objects/ .git/objects/pack/
├─ 08/05455a… README v1 (voll) ├─ pack-3f1c….pack
├─ 7e/… README v2 (voll) │ ├─ README v3 (voll)
├─ a1/… README v3 (voll) │ ├─ README v2 = Δ gegen v3
└─ … │ └─ README v1 = Δ gegen v2
└─ pack-3f1c….idx (ID → Offset)Neue Objekte landen zunächst einzeln („lose“) in .git/objects/xx/…. Bei git gc (auch automatisch) und bei jeder Übertragung per clone, fetch oder push fasst Git sie in Packfiles zusammen. Darin dürfen Objekte als Delta gegen ein ähnliches Objekt gespeichert werden – typischerweise bleibt die neueste Version vollständig, ältere werden als Differenz abgelegt.
Wichtig: Das ist nur eine Speicheroptimierung. Logisch bleibt jeder Commit ein vollständiger Schnappschuss. Nachsehen mit git count-objects -vH und git verify-pack -v.
Quellen: Pro Git, Kapitel 10 „Git Interna“ (git-scm.com/book/de/v2), git-scm.com/docs/git-hash-object, git-init, gitrepository-layout, gitignore. Hash-Werte lokal mit Git 2.55 geprüft.