Knowing the object model explains why branches are cheap, why history rewriting changes IDs, and how recovery works.
At its core Git is a content-addressed key-value store. Every object is stored under the hash of its content (SHA-1 by default, with collision detection; repositories can opt into SHA-256 with git init --object-format=sha256). Same content, same ID, stored once.
There are four object types:
- blob: the raw contents of a file. No name, no permissions; two identical files anywhere in history are one blob.
- tree: a directory listing; each entry has a mode (
100644file,100755executable,040000subtree,120000symlink,160000submodule), a name and an object ID. - commit: points to one root tree (the snapshot), zero or more parent commits, author and committer with timestamps, and the message.
- tag (annotated): points to an object, with tagger, message and optional signature.
An object's ID is the hash of a header like blob 12\0 followed by its content. Because a commit contains its tree ID and parent IDs, its hash transitively covers the entire snapshot and all of history: a Merkle structure. Changing one byte anywhere changes every descendant commit's ID. That is why rebases produce new commits and why a commit hash identifies the full history behind it.
You can explore with plumbing commands:
git cat-file -t <id>gives the type;-ppretty-prints the content.git ls-tree HEADlists the root tree.git hash-object -w filestores a blob and prints its ID.git rev-parse HEAD^{tree}resolves to a commit's tree.
Branches and tags (refs) are just names pointing into this graph, so everything else in Git is built on these few objects.
Example
git cat-file -p HEAD
# tree 8f3e1a...
# parent 3f9a2c1...
# author Ada <ada@example.com> 1760000000 +0200
# committer Ada <ada@example.com> 1760000000 +0200
#
# Add search index
git cat-file -p 'HEAD^{tree}'
# 100644 blob 5d1f0c... README.md
# 040000 tree 2b7e94... src
echo 'hello' | git hash-object --stdin # ce013625030ba8dba906f756967f9e9ca394464a
printf 'blob 6\0hello\n' | sha1sum # same ID: header + content
git cat-file -t ce01362 # blobRemember
- Four object types: blob (content), tree (directory), commit, annotated tag
- Object ID = hash of type header + content: identical content is stored once
- Commits hash their tree and parents, so one ID pins all history
- cat-file -p, ls-tree and hash-object let you inspect it directly
Watch out for
- Thinking Git stores diffs between versions — objects are full snapshots; deltas only appear later as a packfile compression detail.