4.5.7Software Engineering

Git internals — objects (blob, tree, commit, tag), DAG structure

2,089 words9 min readdifficulty · medium

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?


Figure — Git internals — objects (blob, tree, commit, tag), DAG structure

Worked Example 1 — what one git commit creates

Project has one file hello.txt containing hi\n.

  1. Blob. Content hi\nstore = "blob 3\0hi\n" → some hash B. Why this step? The file's bytes get their own identity, independent of the name.
  2. Tree. One line: 100644 blob B<TAB>hello.txt → hashed → tree T. Why this step? The name hello.txt and its permission 100644 live in the tree, not the blob — so renaming a file changes the tree but reuses the blob.
  3. Commit. Points to tree T, has no parent (first commit), author + message → commit C. Why this step? C is a full snapshot: from C you 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.

  1. Blob for notes.txt? Content is hi\n → same storesame hash B. No new blob is stored. Why this step? Content-addressing dedups: identical bytes ⇒ identical key.
  2. New tree T2 now 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.
  3. New commit C2 with tree=T2 and parent=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?
blob (file content), tree (directory listing), commit (snapshot + parents), tag (named annotated pointer)
What does a blob store and NOT store?
Stores raw file bytes; does NOT store the filename, path, or permissions.
Where are filenames and file modes stored in Git?
In the tree object, as entries (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)?
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
What does a commit object point to?
One root tree + zero/one/many parent commits + author/committer + message.
How many parents does a root commit / normal commit / merge commit have?
0 / 1 / 2 (or more).
Why is Git history a DAG and not a tree or general graph?
Directed (child→parent) and acyclic because a parent's hash is fixed before its child is created; merges make it fan in.
Is a branch a Git object?
No — it's a movable pointer (a file in refs/heads containing one commit hash).
Why does copying identical file content create no new blob?
Content-addressing: same bytes → same SHA-1 → same key → stored once (deduplication).
Does Git store diffs between commits?
No, each commit is a full tree snapshot; delta compression happens later only inside packfiles.
What makes Git history tamper-evident?
A commit's hash depends on its tree and parent hashes, so editing old history changes every descendant hash.

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

hash of content

enables

points to root

points back to

contains

contains sub

points to

points to

linked into

acyclic because parent precedes child

Content addressing SHA-1 key

blob raw file bytes

tree directory listing

commit snapshot pointer

tag named signed pointer

branch movable pointer

DAG directed acyclic graph

Immutable history

Automatic dedup + integrity

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!

Go deeper — visual, from zero

Test yourself — Software Engineering