Guide

Git keeps everything it knows in a folder called .git/objects. Each object is stored under an ID, and that ID is a hash of the object's contents. Understanding this one idea explains most of Git's behavior, from why branches are cheap to why history cannot be changed silently.

Concept

Before you start: a hash is a fixed-length fingerprint of some bytes. The same bytes always give the same hash. A tiny change gives a completely different one. Git uses SHA-1 by default.

The four object types

ObjectStoresAnalogy
blobThe raw bytes of one file. No name, no permissionsA file's contents
treeA list of entries: mode, type, hash, name. Points at blobs and other treesA directory listing
commitOne root tree hash, zero or more parent commit hashes, author, committer and messageA snapshot plus its history
tag (annotated)An object hash, a tag name, tagger, message and optional signatureA signed label on a commit

A commit is a snapshot, not a diff. It points to a complete tree of the project. Git computes diffs on demand by comparing two trees.

Walking through it: an ID you can reproduce

The ID of a blob is the hash of a short header plus the contents. You can compute it yourself:

Bash
$ echo 'hello world' | git hash-object --stdin
3b18e512dba79e4c8300dd08aeb37f8e728b8dad

$ printf 'blob 12\0hello world\n' | shasum      # "blob <size>\0<content>"
3b18e512dba79e4c8300dd08aeb37f8e728b8dad  -

Same bytes in, same ID out, on every machine. Now commit a file and look inside:

Bash
$ git cat-file -p HEAD
tree 68aba62e560c0ebc3396e8ae9335232cd93a3f60
author Ada <ada@example.com> 1767225600 +0000
committer Ada <ada@example.com> 1767225600 +0000

Add hello

$ git cat-file -p 'HEAD^{tree}'
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad	hello.txt

The commit points to a tree, and the tree points to the blob with the file's contents. Notice that the commit has no diff in it.

What content addressing buys you

  • Deduplication. Two identical files anywhere in history are one blob.
  • Integrity. A commit's hash covers its tree, which covers every blob, and its parents, which cover their trees, all the way back. Change one byte in history and every descendant hash changes. This structure is a Merkle DAG, the same idea used in certificate transparency and blockchains.
  • Cheap comparisons. If two trees have the same hash, everything under them is identical. git diff and git status skip whole directories.

Storage on disk

A new object is written as one zlib-compressed file. The first two hex characters of its hash become a folder name, so 3b18e512... lives at .git/objects/3b/18e512.... Later, git gc packs objects into packfiles. A packfile stores similar objects as deltas against each other and compresses them, so history stays small.

Note

Git uses SHA-1. After the 2017 SHAttered collision, Git switched to a hardened SHA-1 implementation that detects collision attacks. It has supported a SHA-256 object format since version 2.29 (git init --object-format=sha256). Most repositories still use SHA-1, because hosting support is uneven as of 2025 to 2026.

Common mistakes

  • Thinking a commit stores a diff. It stores a full tree. Diffs are computed when you ask for them.
  • Assuming hashes are for secrecy. They are for identity and integrity, not for hiding content.
  • Expecting a rewrite to leave history unchanged. Any change to a commit gives it a new hash, and every descendant gets a new hash too.

Check yourself

  • You change one line in a file three commits back. Which hashes change? That blob, the trees above it, the commit that contains it, and every later commit.
  • Why is storing ten copies of the same file cheap? Each identical file is the same blob, stored once.

Key takeaways

  • Git has four object types: blob, tree, commit and tag. Each one is stored under the hash of its contents.
  • A commit is a snapshot that points to a complete tree and its parents. Diffs are computed on demand.
  • Content addressing gives deduplication, integrity and fast comparisons. A change to history rewrites every hash after it.

Further learning

Next, see how branches and HEAD are just pointers into this store, in refs, HEAD and the reflog.

Inside .git (Julia Evans) walks through the same folder with examples you can run.