Git Fundamentals¶
What Git actually stores¶
The single most useful mental model shift: Git does not store diffs between versions (that's how older systems like SVN worked). Every commit stores a full snapshot of the entire project at that point — efficiently, because identical file content is only ever stored once (deduplicated by content hash), but conceptually it's a complete snapshot, not an incremental change.
Three object types: a blob is raw file content (no filename — just
bytes, identified by the hash of those bytes). A tree is a
directory listing — filenames mapped to blob or sub-tree hashes. A
commit points to one tree (the project's complete state at that
moment) plus its parent commit(s) plus metadata (author, message,
timestamp). Note in the diagram: main.go's blob is unchanged between
commits, so Commit 2's tree points at the same blob object as before
for the unchanged file (README.md) — Git only creates new objects for
content that actually changed, which is the deduplication that makes
"full snapshot every commit" cheap in practice rather than
exponentially wasteful.
Branches are just pointers¶
A branch is not a container that holds commits — it's a small file
containing one commit hash, and it's called "the tip of the branch."
HEAD is (usually) a pointer to the current branch, which points to a
commit.
git commit creates a new commit object (pointing at the current
branch's tip as its parent), then moves the current branch's pointer
forward to the new commit — that's the entire operation. This is why
creating a branch (git branch feature) is instant regardless of
repository size: it's writing one small file containing a commit hash,
not copying any project files.
Worked example: what git commit actually does¶
Starting state: main points at Commit 2. Working directory has one
changed file. git add computes that file's blob hash and stages it
(records it in the index, a staging-area file). git commit:
- Builds a new tree object from the index — a snapshot of what every file's blob hash currently is, unchanged files reusing existing blob objects.
- Creates a new commit object: parent = Commit 2 (main's current tip), tree = the new tree just built, plus message/author/timestamp.
- Moves
main's pointer to this new commit.
Nothing here is a "diff computed and stored" — the diff shown by
git show or git log -p is computed on demand, by comparing two
trees, not stored as part of the commit itself.
Common pitfall¶
Treating a commit hash as if it identifies "a change" rather than "an
entire project state" leads to real confusion when reasoning about
git diff, git revert, or git cherry-pick — all of them work by
comparing or replaying full snapshots, and understanding that is what
makes their actual behavior (and their failure modes, like conflicts)
predictable instead of mysterious. See
Branching and Merging for where this
snapshot model directly explains what merge and rebase are doing.