ReadbackCourseReference · freeSign in
Reference6 sections · built in Lesson 01

THE OBJECTMODEL

One page. Everything git stores, the refs that point into it, and the commands that let you look at all of it directly.

The four object types

Everything git stores is one of four objects, each named by the SHA of its own contents. Change a byte, get a different object. Nothing is ever edited in place (Pro Git §10.2).

blob

The contents of one file. No filename, no permissions, no history — just bytes.

Two identical files anywhere in the repo, at any point in history, are the same blob stored once.

tree

A directory listing: mode · type · hash · name per entry. Entries point at blobs (files) or other trees (subdirectories).

A tree is the snapshot of one directory. The root tree is the snapshot of the project.

commit

One root tree, zero or more parent hashes, an author, a committer, and a message.

0 parents = root commit. 1 = ordinary. 2 or more = merge.

tag (annotated)

A named, message-carrying, optionally signed pointer at another object — usually a commit.

Lightweight tags are not objects at all; they are just refs.

Commit anatomy

tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904the whole snapshot, not a diff
parent 7d2e4033a1c9b0e5f1d2a8c73e0b6a49f5c81d3eabsent on root; twice on a merge
author Ada Lovelace <ada@ex.com> 1753900000 +0100who wrote it; survives rebase
committer Ada Lovelace <ada@ex.com> 1753900000 +0100who applied it; rewritten by rebase
 blank line, mandatory
Add rate limiting to the API clientmessage: everything after it
The literal bytes of a commit object, as git cat-file -p HEAD prints them. There is no field for "the diff", because there is no diff.

The five invariants

  1. A commit is a snapshot, not a diff. Every diff you have ever seen in git was computed on demand by comparing two trees. Storage deduplicates unchanged blobs and later packs objects as deltas — but that is compression, not identity.
  2. The hash is derived from the content, including the parent hash. So a commit's identity encodes its entire ancestry.
  3. Rewriting anything renumbers everything above it. Change a commit and every descendant gets a new hash, because each one contains the hash below it. That is rebase, amend, squash and cherry-pick in one sentence.
  4. Pointers only run backwards. A commit names its parent. Nothing names its children. Descendants are found by walking back from known tips.
  5. Nothing is deleted, only abandoned. A commit no ref can reach is unreachable, not gone. Default garbage-collection grace is 30 days for unreachable reflog entries, 90 for reachable ones (git-gc).

Refs, and HEAD

Objects are immutable. Refs are the only mutable thing in a repository, and they are files (Pro Git §10.3).

FileContents
.git/refs/heads/mainOne 40-character commit hash plus a newline — 41 bytes. That is the whole branch.
.git/HEADNormally the text ref: refs/heads/main — a reference to a branch, not to a commit. Detached, it holds a raw hash instead.
.git/refs/tags/v1.0A hash. Points at a commit for a lightweight tag, or at a tag object for an annotated one.
.git/packed-refsThe same refs, collected into one file after git gc. Look here when refs/heads/ seems empty.
What actually moves

git commit writes a new commit object and overwrites the current branch file with its hash. HEAD does not change — it names the branch, not the commit. git switch does the opposite: it changes HEAD to name a different branch and rewrites the working tree, and moves no branch file at all.

Inspection commands

Plumbing — look at raw objectsShows
git cat-file -p <ref>The raw contents of any object, pretty-printed.
git cat-file -t <ref>Its type: blob, tree, commit or tag.
git cat-file -p 'HEAD^{tree}'The root tree of HEAD — the snapshot's top-level directory listing.
git rev-parse <ref>Resolve any reference expression to a full 40-character hash.
git hash-object -t commit --stdinCompute — without writing — the hash of the content you pipe in.
git ls-tree -r HEADEvery file in the snapshot, recursively, with its blob hash.
Porcelain — what you'll actually typeShows
git log --graph --oneline --decorate --allThe shape of the whole graph, every branch, one line each. The auditing command.
git show --stat <ref>One commit: metadata plus which files changed and by how much.
git show <ref>:<path>The contents of one file as of one commit.
git log --format='%h %an %ad %cn %cd' --date=shortAuthor against committer, side by side. Divergence means history was replayed.
git log --oneline <a>..<b>Commits reachable from b but not a. The basis of “what is on this branch”.
git merge-base <a> <b>The commit a merge of these two would use as its reference. Neither branch.

Naming a commit

Anywhere git wants a commit, all of these work. They resolve to a hash before anything else happens (gitrevisions(7)).

ExpressionMeans
HEADThe commit currently checked out.
c81b95Any unambiguous hash prefix — usually 7 or more characters.
HEAD~3Three steps back along first parents. “Three commits ago.”
HEAD^The first parent. HEAD^2 is the second parent — only meaningful on a merge.
main@{2.days.ago}Where main pointed two days ago, per the reflog.
HEAD^{tree}The tree object of that commit, rather than the commit itself.
origin/main..HEADEverything you have that the remote does not. What a push would send.

~ walks backwards through generations, always taking the first parent. ^ picks which parent of a single commit you mean. On a linear history the two behave identically, which is why the difference stays hidden until you are standing on a merge commit and need the other side.

A4 or Letter
one ink, no chrome
Sources