Git Internals: Merkle DAGs, Content-Addressable Blobs & 41-Byte Branches
Why Linus Torvalds built Git not as a chain of file diffs, but as an immutable cryptographic snapshot filesystem where branching takes O(1) time.
Most developers experience Git as a confusing set of CLI incantations only because they picture version control as a stack of line-by-line file diffs. When Linus Torvalds wrote Git in ten days in April 2005 to manage the Linux kernel, he threw out file-diff chains completely and built a cryptographic content-addressable filesystem ('.git/objects') wired into an immutable Directed Acyclic Graph (Merkle DAG). Every file ('blob'), directory ('tree'), and project snapshot ('commit') is named solely by the cryptographic hash of its exact byte contents — meaning a single altered bit 50,000 commits ago cascades upward to change the root commit hash today! Once history is an immutable Merkle graph of snapshots, creating a 'branch' drops from copying gigabytes on a server to writing a single 41-byte hash pointer file in microseconds, while background packfiles compress snapshots retrospectively into tiny deltas.
- 1.Snapshots, Not Diff Chains: Unlike CVS/SVN per-file patch lists, every Git commit points to a complete logical snapshot of the entire working tree via immutable content-addressed objects inside '.git/objects'.
- 2.Four Object Types & Automatic Deduplication: 'blob' (nameless raw file bytes), 'tree' (filenames + POSIX modes + child hashes), 'commit' (root tree hash + parent commit hashes + metadata), and 'annotated tag' — identical files anywhere share one blob.
- 3.Merkle DAG Avalanche Integrity: Because trees hash child hashes and commits hash root trees plus parent commit hashes, trusting a single 40-hex SHA-1 (or 64-hex SHA-256) HEAD hash mathematically verifies every file and commit back to day one.
- 4.O(1) 41-Byte Branches & Retrospective Packfiles: A branch is literally a 41-byte file in '.git/refs/heads/' holding a commit hash; '.pack' files run sliding-window xdelta + zlib compression under the hood so full snapshots consume minimal disk space.
Interactive Lab: Binary Logic & Deterministic State
First-Principles Mechanism (Step by Step)
1. Content-Addressable Storage: Naming Every Byte by Its Cryptographic Hash
Traditional filesystems map a mutable location name ('src/main.rs') to blocks on disk that get silently overwritten in place. Git's low-level 'plumbing' layer inside '.git/objects' works like an append-only cryptographic key-value store: hand Git any payload of bytes, and it prepends a tiny ASCII header ('<type> <byte_length>\0'), computes a 160-bit SHA-1 digest (40 hexadecimal characters, or 256-bit SHA-256 in Git's newer object format), compresses the result with zlib deflate, and writes it to disk using the first 2 hex characters as a folder and the remaining 38 as the filename! Two profound consequences follow immediately. First, objects are strictly immutable: you cannot edit an object without changing its hash, which creates a brand-new address while leaving the old state intact. Second, deduplication is automatic and global: if 500 commits or 12 subdirectories contain the exact same 10 MiB asset, its cryptographic hash is identical, so Git stores that 10 MiB 'blob' on disk exactly once!
2. Blobs, Trees & Commits: Building the Immutable Merkle DAG
How does Git represent complex nested folders and multi-author timelines out of pure hash-addressed chunks? Using just four object types wired into a Ralph Merkle tree / Directed Acyclic Graph (DAG)! A 'blob' holds raw file bytes — and shockingly, a blob stores ZERO filename, ZERO directory path, and ZERO permissions! Names live one level up inside a 'tree' object (analogous to a UNIX directory), which stores a sorted list of entries combining POSIX mode ('100644' normal file, '100755' executable, '040000' nested subtree), filename, and the 20-byte binary hash of the target blob or child tree. Rename a 100 MiB dataset file without editing its contents, and only a tiny hundred-byte 'tree' object changes — the 100 MiB blob hash stays untouched! Above the root tree sits a 'commit' object (~250 bytes of plain text): it hashes together the single root 'tree' hash, zero or more 'parent' commit hashes (1 parent = normal commit, 2+ parents = merge commit joining divergent branches without cycles!), author/committer timestamps, and message. Flip one bit in any source file from 2012, and the Merkle hash avalanche alters blob → parent trees → 2012 commit → every descendant commit hash up to today!
3. Why Git Branches Cost 41 Bytes & Never Lose 'Deleted' Commits Immediately
In older centralized version control, creating a branch meant duplicating server directory metadata across thousands of files — an O(N) operation so heavy teams feared branching. In Git's Merkle DAG, what is a branch physically? Open '.git/refs/heads/main' in a text editor: it is literally a 41-byte file containing 40 hex characters of a commit hash plus a newline! And '.git/HEAD' is just a 21-byte symbolic pointer reading 'ref: refs/heads/main'. Creating a new feature branch on the 80,000-file Linux kernel repository takes O(1) microseconds because Git merely writes 41 bytes! Furthermore, because commit objects are immutable, scary-sounding commands become crystal clear: 'git commit' mints one new node pointing back to the old HEAD and advances your 41-byte branch file; 'git reset --hard' moves that 41-byte pointer to another commit; 'git rebase' replays changes onto a new base by minting fresh commit hashes while leaving your original commits completely unharmed inside '.git/objects' — easily rescued for weeks via '.git/logs/HEAD' (the local reflog) until periodic garbage collection!
4. The Snapshot Paradox Solved: Packfiles & Inverted xdelta Compression
Here every sharp engineer pauses to object: 'If editing 1 line of a 5 MiB source file creates a brand-new 5 MiB blob on every commit, shouldn't 1,000 commits bloat '.git' to 5 gigabytes?!' Initially, yes — Git writes fast 'loose objects' compressed individually with zlib so 'git add' and 'git commit' stay instantaneous. Then, during 'git gc' or before streaming over the network on 'git push'/'git fetch', Git separates its logical snapshot model from physical disk layout by sweeping loose objects into indexed '.pack' packfiles! Inside a packfile, Git heuristically groups objects by filename, type, and size, slides a binary 'xdelta' window across neighboring versions, and stores most blobs as tiny byte-level copy/insert instructions referencing a base object! Cleverly, whereas old RCS/SVN systems stored version 1 whole and forced you to apply 999 forward patches to reconstruct today's code, Git packfiles invert the arrow: they keep recent HEAD blobs whole for blazing-fast checkouts and encode older historical revisions as compact backward deltas!
Common Myths vs. Verified Reality (Audited by SkepticJudge)
Suppose you rename a 200 MiB video file inside a Git repository ('mv demo.mp4 archive/intro.mp4') and commit the change without modifying a single byte of the video itself. What new data does Git create inside '.git/objects'?
Verified Primary Lectures & Sources (90+ Brain-Score)
Browse All 10,534 Sources →Git Will Finally Make Sense After This
- • Git is a snapshot database where every commit stores a full project tree pointer, metadata, and parent links forming a directed graph.
- • Branches and HEAD are lightweight movable pointers to commits, making branch creation instantaneous and explaining detached HEAD state.
The Mind Behind Linux | Linus Torvalds | TED
- • Linus Torvalds built both the Linux kernel and Git by pragmatically solving concrete engineering bottlenecks as collaboration scaled.
- • He defines good taste in code via linked-list pointers: refactoring away special-case branches so boundary cases become normal logic.
What's the algorithm behind git diff?
- • Computing minimal line insertions and deletions between two text files reduces directly to finding their Longest Common Subsequence (LCS).
- • Defining dynamic programming subproblems over array prefixes shrinks state complexity to O(n^2), solved iteratively in topological order.
How Git and Cursor sync code (Merkle trees are super cool!)
- • Merkle trees hash individual files or file chunks at leaf nodes and combine child hashes upward into a single root digest.
- • Comparing client and server root hashes detects any codebase change immediately without scanning or copying every file.
Viele Entwickler empfinden Git nur deshalb als rätselhafte Sammlung kryptischer Terminal-Befehle, weil sie sich Versionsverwaltung innerlich als Kette zeilenweiser Text-Diffs vorstellen. Als Linus Torvalds Git im April 2005 in nur zehn Tagen für den Linux-Kernel entwarf, warf er dieses Diff-Denken komplett über Bord und baute stattdessen ein kryptografisch inhaltsadressiertes Dateisystem (‚.git/objects‘) in Form eines gerichteten azyklischen Graphen (Merkle-DAG). Jede Datei (‚blob‘), jedes Verzeichnis (‚tree‘) und jeder Projekt-Schnappschuss (‚commit‘) wird ausschließlich unter dem kryptografischen Hash-Wert seines exakten Byte-Inhalts gespeichert — kippt ein einziges Bit vor 50.000 Commits um, ändert sich unweigerlich der heutige Wurzel-Hash! In diesem unveränderlichen Graphen schrumpft ein neuer ‚Branch‘ vom Kopieren ganzer Ordner auf das Schreiben einer winzigen 41-Byte-Textdatei in Mikrosekunden, während Packfiles Speicherplatz nachträglich per Delta-Kompression einsparen.
- 1.Schnappschüsse statt Patch-Ketten: Anders als ältere Systeme (CVS/SVN) speichert Git logisch keine Differenzlisten pro Datei, sondern verweist in jedem Commit über unveränderliche Objekte in ‚.git/objects‘ auf einen kompletten Projekt-Zustand.
- 2.Vier Objekttypen & automatische Deduplizierung: ‚blob‘ (namenlose Rohbytes), ‚tree‘ (Dateinamen + Rechte + Kind-Hashes), ‚commit‘ (Wurzel-Tree + Eltern-Commits + Autor/Zeit) und ‚tag‘ — identische Dateien im gesamten Projekt teilen sich denselben Blob.
- 3.Kryptografische Merkle-Lawine: Da jeder Tree die Hashes seiner Kinder und jeder Commit seinen Wurzel-Tree samt Eltern-Hashes einrechnet, beglaubigt ein einziger 40-stelliger SHA-1- (bzw. SHA-256-)Hash lückenlos die gesamte Historie.
- 4.41-Byte-Branches in O(1) & nachträgliche Packfiles: Ein Branch ist schlicht eine 41 Byte winzige Textdatei unter ‚.git/refs/heads/‘; im Hintergrund verdichtet ‚git gc‘ ähnliche Objekte per xdelta und zlib in kompakte ‚.pack‘-Archive.
Interaktives Labor: Binäre Logik & deterministische Zustände
Wie es Schritt für Schritt wirklich funktioniert
1. Inhaltsadressierter Speicher: Warum der Inhalt seine eigene Adresse ist
Klassische Dateisysteme verknüpfen einen veränderlichen Pfadnamen (‚src/main.rs‘) mit Speicherblöcken, die beim Speichern an Ort und Stelle überschrieben werden. Gits Fundament im Ordner ‚.git/objects‘ arbeitet genau umgekehrt als unveränderlicher kryptografischer Schlüssel-Wert-Speicher: Übergibt man Git beliebige Datenbytes, stellt es einen kurzen Header voran (‚<Typ> <Byte-Länge>\0‘), berechnet daraus die 160-Bit-Prüfsumme per SHA-1 (40 Hexadezimalzeichen, bzw. 256-Bit-SHA-256 im modernen Objektformat), komprimiert das Paket per zlib und legt es unter den ersten 2 Hex-Zeichen als Ordner und den restlichen 38 Zeichen als Dateinamen ab! Daraus folgen sofort zwei mächtige Eigenschaften: Erstens ist jedes gespeicherte Objekt absolut unveränderlich — wer auch nur ein Komma editiert, erzeugt zwangsläufig einen neuen Hash an einer neuen Adresse, ohne den alten Stand je zu zerstören. Zweitens dedupliziert Git vollautomatisch: Liegt exakt dieselbe 10-MiB-Grafik in 500 Commits oder zwölf Unterordnern, ergibt sie immer denselben Hash und belegt genau einmal Platz!
2. Blobs, Trees & Commits: Wie der kryptografische Merkle-DAG wächst
Wie formt Git aus simplen Hash-Paketen tief verschachtelte Ordnerbäume und verzweigte Projektgeschichten? Mit lediglich vier Objekttypen, verknüpft zu einem gerichteten azyklischen Merkle-Graphen (Merkle-DAG)! Ein ‚blob‘ enthält ausschließlich die nackten Inhaltsbytes einer Datei — er kennt weder ihren Dateinamen noch ihren Ordnerpfad oder ihre Zugriffsrechte! Namen wohnen eine Ebene höher im ‚tree‘-Objekt (dem Gegenstück eines Verzeichnisses): Es listet sortiert Dateimodus (‚100644‘ Datei, ‚100755‘ ausführbar, ‚040000‘ Unterordner), Klartextnamen und den Binär-Hash des zugehörigen Blobs oder Unter-Trees auf. Benennt man eine 100 MiB große Datei um, entsteht lediglich ein winziger neuer Tree — der 100-MiB-Blob bleibt unangetastet! Über dem obersten Wurzel-Tree thront schließlich das ‚commit‘-Objekt (~250 Byte Text): Es bündelt den Hash des Wurzel-Trees, null oder mehr ‚parent‘-Commit-Hashes (1 Elternteil = linearer Schritt, 2+ Eltern = Merge-Commit zweier Entwicklungslinien ohne Rückschleifen!), Autor samt Zeitstempel sowie die Commit-Nachricht (ergänzt durch signierbare ‚tag‘-Objekte für Releases). Kippt irgendwo ein einziges Bit in einer Datei aus dem Jahr 2012 um, rollt eine kryptografische Lawine über Blob → Trees → 2012er-Commit durch sämtliche Nachfolge-Hashes bis in die Gegenwart!
3. Warum ein Branch nur 41 Byte wiegt & ‚Rebase‘ nichts überschreibt
In älteren zentralen Systemen bedeutete ein neuer Entwicklungszweig oft aufwendige Server-Kopierarbeit über tausende Dateipfade — eine O(N)-Operation, vor der Teams zurückschreckten. Was aber ist ein Branch physisch in Gits Merkle-DAG? Öffnet man ‚.git/refs/heads/main‘ im Editor, blickt man auf eine exakt 41 Byte kurze Textdatei: 40 Hex-Zeichen eines Commit-Hashes plus Zeilenumbruch! Und ‚.git/HEAD‘ enthält schlicht den Verweis ‚ref: refs/heads/main‘. Selbst im Linux-Kernel mit über 80.000 Quelldateien dauert das Anlegen eines Branches konstante O(1)-Mikrosekunden, weil Git bloß 41 Byte schreibt! Zugleich verlieren gefürchtete Befehle jeden Schrecken, sobald man die Unveränderlichkeit der Knoten bedenkt: ‚git reset --hard‘ biegt lediglich diesen 41-Byte-Zeiger um; ‚git rebase‘ überschreibt niemals existierende Commits, sondern berechnet neue Commit-Knoten auf neuer Basis (mit neuen Hashes!), während die alten Knoten unversehrt in ‚.git/objects‘ liegen bleiben — jederzeit binnen Sekunden über ‚.git/logs/HEAD‘ (das Reflog) rettbar, bis Wochen später die Speicherbereinigung anläuft!
4. Das Snapshot-Paradoxon gelöst: Packfiles & umgekehrte Delta-Kompression
An dieser Stelle hakt jeder aufmerksame Ingenieur nach: ‚Wenn das Ändern einer einzigen Codezeile in einer 5-MiB-Datei bei jedem Commit einen komplett neuen 5-MiB-Blob erzeugt, müsste das Repository nach 1.000 Commits doch auf 5 Gigabyte explodieren?!‘ Im ersten Moment legt Git tatsächlich einzelne zlib-komprimierte ‚Loose Objects‘ an, damit ‚git commit‘ ohne Verzögerung blitzt. Doch beim Aufräumen per ‚git gc‘ oder vor jeder Netzwerkübertragung (‚git push‘ / ‚git clone‘) trennt Git sein logisches Snapshot-Modell strikt vom physischen Speicherformat und bündelt tausende Objekte in indizierte ‚.pack‘-Dateien (Packfiles)! Darin sortiert Git verwandte Blobs geschickt nach Dateiname und Größe, lässt einen binären ‚xdelta‘-Algorithmus über ähnliche Versionen gleiten und speichert fast alle Stände als winzige Kopier-Anweisungen relativ zu einem Basis-Objekt! Der besondere Clou im Vergleich zu alten Diff-Systemen: Während frühe Systeme Version 1 im Volltext speicherten und für den aktuellen Code mühsam 999 Patches vorwärts addieren mussten, drehen Git-Packfiles den Spieß um — der aktuelle HEAD liegt sofort abrufbar im Volltext vor, während ältere Historienstände rückwärts als platzsparende Deltas daranhängen!
Häufige Irrtümer vs. geprüfte Realität (SkepticJudge)
Angenommen, du verschiebst und benennst eine 200 MiB große Videodatei in einem Git-Repository um (‚mv demo.mp4 archiv/intro.mp4‘) und committest, ohne ein einziges Byte des Videos zu verändern. Was legt Git intern in ‚.git/objects‘ neu an?
Geprüfte Primär-Vorträge im DeepScroll-Index
Alle 10.534 Quellen →Git Will Finally Make Sense After This
- • Git speichert vollständige Projekt-Snapshots als Commits, die über Elternzeiger einen gerichteten azyklischen Graphen (DAG) bilden.
- • Branches und HEAD sind leichtgewichtige Zeiger auf Commits, weshalb neue Zweige sofort entstehen und Detached-HEAD-Zustände auftreten.
The Mind Behind Linux | Linus Torvalds | TED
- • Linus Torvalds schuf den Linux-Kernel und Git aus pragmatischer Ingenieursarbeit, um reale Engpässe tausender Entwickler zu lösen.
- • Guten Code-Geschmack zeigt er an verketteten Listen: Zeigerlogik ersetzt Sonderfall-Abfragen, sodass Randfälle im Normalfall aufgehen.
What's the algorithm behind git diff?
- • Die minimale Anzahl eingefügter und gelöschter Zeilen zwischen zwei Textdateien lässt sich als längste gemeinsame Teilfolge formulieren.
- • Durch Teilprobleme über Array-Präfixe sinkt der Zustandsraum der dynamischen Programmierung auf O(n²) in klarer topologischer Ordnung.
How Git and Cursor sync code (Merkle trees are super cool!)
- • Merkle-Bäume hashen einzelne Dateien oder Dateiblöcke in Blattknoten und fassen Kinder-Hashes bis zur Wurzel zusammen.
- • Ein Abgleich der Wurzel-Hashes zwischen Client und Server erkennt Codeänderungen sofort ohne vollständigen Dateiscan.
Connected AI-Wiki Articles
Built with specialist AI agents, allied research bots & learner support to keep humanity's knowledge 100% ad-free forever.