Git internals — objects (blob, tree, commit, tag), DAG structure
WHY does Git work this way?
The whole model is: objects never change; you only ever add new ones. Branches are just movable pointers into this immutable pile of objects.
WHAT are the four objects?
HOW the objects link — the DAG

Worked Example 1 — what one git commit creates
Project has one file hello.txt containing hi\n.
- Blob. Content
hi\n→store = "blob 3\0hi\n"→ some hashB. Why this step? The file's bytes get their own identity, independent of the name. - Tree. One line:
100644 blob B<TAB>hello.txt→ hashed → treeT. Why this step? The namehello.txtand its permission100644live in the tree, not the blob — so renaming a file changes the tree but reuses the blob. - Commit. Points to tree
T, has no parent (first commit), author + message → commitC. Why this step?Cis a full snapshot: fromCyou can reach every byte of the project.
commit C ──tree──▶ tree T ──hello.txt──▶ blob B ("hi\n")
Worked Example 2 — second commit, deduplication in action
Now you add notes.txt = hi\n (same content!) and commit again.
- Blob for notes.txt? Content is
hi\n→ samestore→ same hashB. No new blob is stored. Why this step? Content-addressing dedups: identical bytes ⇒ identical key. - New tree
T2now lists two entries (hello.txt→B, notes.txt→B). New tree hash. Why? The directory listing changed, so the tree's bytes changed, so its hash changed. - New commit
C2withtree=T2andparent=C. Why? The parent link is what makes the DAG edge and gives you history.
C2 ──parent──▶ C
└─tree─▶ T2 ─▶ B (shared!)
Common mistakes (Steel-manned)
Flashcards
What are Git's four object types?
What does a blob store and NOT store?
Where are filenames and file modes stored in Git?
(mode, type, hash, name).How is a Git object's SHA-1 computed?
SHA1("<type> <size>\0" + content) — header is included.What is the SHA-1 of an empty file (blob)?
What does a commit object point to?
How many parents does a root commit / normal commit / merge commit have?
Why is Git history a DAG and not a tree or general graph?
Is a branch a Git object?
Why does copying identical file content create no new blob?
Does Git store diffs between commits?
What makes Git history tamper-evident?
Recall Feynman: explain to a 12-year-old
Imagine a magic toy box. Every time you put a toy in, the box prints a sticker that is a secret code made from the toy's exact shape. Same toy ⇒ same sticker, always. Blobs are toys (the actual stuff). A tree is a labelled tray that says "sticker #B is called hello.txt". A commit is a photo of the whole room plus a note saying "the photo before this one was that one over there." Because each photo points to the older photo, you can walk backwards through time — but never in a circle, because you can't take a photo of a photo that doesn't exist yet. To save a new version, Git never erases old toys; it just snaps a new photo. That's why deleting a branch is just throwing away a bookmark, not the photos.
Connections
- Hashing & SHA-1 — content-addressing relies on collision-resistant hashes.
- Merkle Trees — Git's tree-of-hashes is a Merkle tree; same integrity property powers blockchains.
- Branches and HEAD — refs as movable pointers into the DAG.
- Packfiles and delta compression — how snapshots are stored efficiently.
- Garbage Collection (git gc) — unreachable objects pruned from the DAG.
- Directed Acyclic Graphs — topological sort, ancestors/descendants.
- Immutability and Persistent Data Structures — append-only design.
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, Git ko samajhne ka sabse asaan tareeka hai: ye ek content-addressable filesystem hai. Matlab har cheez ka naam uske content ka SHA-1 hash hota hai. Agar do files mein bilkul same bytes hain, to dono ka hash same aayega, aur Git use sirf ek baar store karega — ye automatic deduplication hai. Aur ek byte bhi badlo to hash badal jaata hai, isliye corruption turant pakda jaata hai.
Char hi objects hote hain: blob = file ke andar ke raw bytes (naam nahi hota isme!), tree = directory listing yaani (mode, type, hash, name) ki lines (filename yahan hota hai), commit = poore project ka snapshot, jo ek root tree aur apne parent commit ko point karta hai, aur tag = ek naam wala signed pointer. Yaad rakho: filename blob mein nahi, tree mein hota hai — isiliye file rename karne par naya blob nahi banta.
Commit, parent ko point karta hai, isliye history ek DAG (Directed Acyclic Graph) banti hai — directed kyunki arrow child se parent ki taraf jaata hai, aur acyclic kyunki parent ka hash pehle se fix hota hai, to circle ban hi nahi sakta. Merge commit ke do parents hote hain (history fan-in karti hai). Branch koi object nahi hota — wo bas ek pointer file hai jisme ek commit ka hash likha hota hai.
Exam aur real coding dono mein ye faida deta hai: tum samajh jaaoge ki Git diffs store nahi karta, balki full snapshots store karta hai (delta compression baad mein packfile ke andar hoti hai). Aur jab koi bole "history rewrite", to actual mein naye objects banate hain, purane immutable rehte hain. Bas header "blob 3\0" ko hash mein include karna mat bhoolna, warna hand-calculated hash match nahi karega!