Intuition The one core idea
Git names every piece of your project by a fingerprint of its own bytes , and then links those named pieces together with arrows that only ever point backwards in time . Once you understand "a name that is computed from the content" and "arrows that never loop," every other Git word — blob, tree, commit, branch, DAG — is just a small variation on those two ideas.
This page assumes you know nothing . We build every symbol the parent topic uses, one at a time, each one earning the next.
Definition A byte and "content"
A byte is one tiny number from 0 to 255 that a computer stores. A text file like hi\n is just a short list of bytes: the letter h, the letter i, and a "newline" character (written \n) that means "go to the next line." When we say the content of a file, we mean exactly that list of bytes — nothing about its name, nothing about where it lives.
Picture a file as a row of little boxes, each holding one number.
Intuition Why start here?
Every Git word later is built on "content = the list of bytes." A blob is content. A hash is computed from content. If "content" is fuzzy, everything above it is fuzzy. So we nail it first.
Definition Hash (in plain words)
A hash is a machine that eats any list of bytes and spits out a short fixed-length code — for Git, a 40-character string of hex digits like e69de29b…. The magic rules are:
Same bytes in → same code out , every single time, on every computer.
Different bytes in → (almost certainly) a different code.
You cannot run it backwards to guess the bytes from the code.
The specific machine Git uses is called SHA-1 — see Hashing & SHA-1 for the gears inside. For us, treat it as a black box that turns "content" into "a name."
Intuition Why does the topic
need a hash?
Git wants a filing system where the name of a thing is decided by the thing itself , not chosen by a human. A hash is exactly the tool that answers "what unique name does this content deserve?" No other tool gives you automatic, collision-resistant, content-derived names . That is why hashing — not, say, counting 1, 2, 3… — is the right tool here.
Let us read every symbol in that line, because the parent note uses all of them:
type ::: a word telling what kind of object this is (blob, tree, commit, tag).
size ::: the count of content bytes, written as ordinary digits.
\0 ::: one "null byte" (the number 0) that marks where the header ends and content begins.
+ (here) ::: means "stick these byte-lists next to each other," not arithmetic.
Worked example Compute the empty-file name by hand-logic
An empty file has 0 bytes of content. So
store = "blob 0\0"
and SHA1 of exactly those bytes is e69de29bb2d1d6434b8b29ae775ad8c2e48c5391. That famous hash you see everywhere is nothing but the fingerprint of emptiness.
Common mistake "The hash is of the file content only."
Why it feels right: "content-addressed" sounds like just the bytes. Reality: you must hash header + content. Forgetting the "blob 3\0" header is the #1 reason a hand-computed hash won't match Git.
The 40-character name is written in hex ("base 16"): the digits 0–9 plus letters a–f, giving 16 possible symbols per position. Each hex digit stands for a number 0–15. Two hex digits together describe one byte (0–255). So a 40-hex-digit name secretly describes 20 bytes of fingerprint.
Intuition Why hex and not normal numbers?
A raw fingerprint is 20 bytes of arbitrary values, many of which are unprintable (you can't type them). Hex is the tool that turns any byte into two safe, typeable characters. That is the only reason it appears — it is a display choice, not part of the math.
Definition Key–value store
A key–value store is a filing cabinet with two columns: a key (a label) and a value (the stuff). You hand it a key, it hands back the value. Git's twist: the key is always the hash of the value.
Intuition Why the topic calls Git "content-addressable"
Normally you address (find) a file by its location — "folder A, file B." Git addresses files by their content's fingerprint instead. That single choice gives two free gifts:
Deduplication: two files with identical bytes hash to one key, so they're stored once.
Integrity: flip one byte and the key no longer matches — corruption becomes visible .
This is the same idea behind Merkle Trees , where whole trees of data get one summarizing fingerprint.
Git links its named objects with arrows . To read the topic's central picture, you need three graph words.
Definition Graph, directed, acyclic
A graph is just dots (called nodes ) with lines between them.
Directed means each line is an arrow : it goes from one dot to another, and the direction matters.
Acyclic means: if you start on a dot and keep following arrows, you can never return to where you started — there are no loops.
Put together: a DAG (Directed Acyclic Graph) is "dots joined by one-way arrows that never form a circle." See Directed Acyclic Graphs .
Intuition Why must Git's history be
acyclic ?
A commit's name is computed from the names of the things it points to (its tree and its parent). To make a commit, those older names must already exist . You literally cannot build an object whose name depends on a future object that isn't created yet. So arrows can only ever point to the past — and a one-way path into the past can never curl back into a loop. The acyclicity isn't a rule Git enforces with a checker; it's forced by hashing . This is the heart of Immutability and Persistent Data Structures : old objects never change, you only ever add new ones.
With bytes , hash , key–value , and DAG in hand, the four object types are just four flavours of "named content."
Definition The four Git objects (foundations view)
blob — a value that is one file's raw bytes. No name, no permission — pure content.
tree — a value that is a list of lines (mode, type, hash, name), each line pointing to a blob or another tree. This is where filenames live.
commit — a value that points to one root tree plus zero or more parent commits , with author and message. It's a full snapshot + a backward arrow.
tag — a value that is a named pointer to another object (usually a commit) with its own message.
Two more words the parent leans on:
mode ::: a small number like 100644 recording file permissions (readable/executable), stored in the tree line.
snapshot ::: a complete picture of the whole project at one moment — a commit reaches every byte through its tree, so it is a snapshot, not a diff .
Common mistake "Git stores diffs between versions."
Why it feels right: history feels like a chain of changes. Reality: every commit points to a full tree snapshot . Space-saving delta compression happens only later , inside Packfiles and delta compression — an optimization, not the data model.
Definition Branch and HEAD
A branch is not an object. It's a tiny file holding one 40-char commit name — a sticky-note you can peel off and re-stick onto a newer commit. HEAD is a sticky-note pointing at which branch you're currently on . Details in Branches and HEAD .
Intuition Why pointers, not objects?
Objects are frozen forever (immutable). But "where the newest work is" must move as you commit. Git separates the frozen pile of objects from the movable pointers into it. Deleting a branch removes only the sticky-note; the commits linger until Garbage Collection (git gc) sweeps unreachable ones away.
bytes = a list of numbers
hash = fingerprint of bytes
four objects blob tree commit tag
movable pointers branch and HEAD
Each arrow reads "is needed to understand." Notice hashing (B) feeds both the objects and the acyclicity — that is the double duty at Git's core.
Test yourself — cover the right side and answer aloud.
What is the "content" of a file, in Git's meaning? Exactly its list of bytes — no name, no path, no permission.
What does a hash guarantee about identical inputs? Same bytes always produce the same fixed-length code.
Can you run a hash backwards to recover the bytes? No — it is one-way.
What three parts make up store before hashing? type, then a space, size, a \0 byte, then the content.
Why include the header instead of hashing content alone? It makes the name type-aware and length-framed, so different object kinds can't be confused.
What is the SHA-1 of an empty blob and why? e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 — it is SHA1("blob 0\0").
Why are the 40 characters written in hex? To turn arbitrary (often unprintable) fingerprint bytes into safe, typeable symbols.
In a key–value store, what is Git's key? The hash (oid) of the value's bytes.
Name the two free gifts of content-addressing. Deduplication (same bytes stored once) and integrity (corruption changes the key).
What do "directed" and "acyclic" each mean? Directed = one-way arrows; acyclic = following arrows never loops back.
Why is Git's history forced to be acyclic? A commit's hash depends on already-existing objects, so arrows can only point to the past — no future loop is possible.
Where do filenames and modes live? In the tree object's lines, not in the blob.
Is a branch a Git object? No — it's a movable file holding one commit hash; HEAD points to the current branch.