# Git’s object model

> Blobs, trees and commits, each named by the hash of its content — a snapshot store, not a diff store.

Concept · How Git works · https://levelcode.org/concepts/git-object-model

Underneath every Git command is a simple **content-addressed store**. Every object is saved under the hash of its own content, SHA-1 by default with SHA-256 available, in `.git/objects`. There are three object types that matter day to day.

| Object | Holds | Points to |
|---|---|---|
| **Blob** | A file's contents (no name, no permissions) | Nothing |
| **Tree** | A directory listing: names, modes and the hash of each entry | Blobs and other trees |
| **Commit** | Author, committer, date, message | One root tree, and zero or more parent commits |

```text
commit 9f2c…  "Add login form"
 ├─ parent → commit 41ab…
 └─ tree 7d10…
      ├─ README.md     → blob e69d…
      └─ src/          → tree 3c4a…
           └─ login.ts → blob b81f…
```

```bash
git cat-file -p HEAD            # show the commit object
git cat-file -p HEAD^{tree}     # show its tree
```

## Snapshots, not diffs

A commit records the **entire project state** through its tree, not a list of changes. When you run `git diff` or `git show`, Git computes the difference between two snapshots on the fly. This is efficient because:

- **Unchanged files and folders are shared.** If only `login.ts` changed, the new commit's tree reuses the old blob and tree hashes for everything else. Only the changed blob and the trees above it are new.
- **Identical content is stored once,** wherever and however often it appears.
- **Packfiles** compress similar objects against each other on disk, so storage is still compact.

## Why hashes matter

- **Integrity.** A commit's hash covers its tree, which covers every file, plus its parents' hashes, which cover the whole history. Change one byte anywhere in the past and every hash after it changes. This is a [Merkle tree](https://levelcode.org/concepts/merkle-trees), the same structure that lets replicas find their differences quickly.
- **Immutability.** Objects can't be edited, only replaced. "Amending" or "rebasing" a commit creates **new** commits with new hashes. That's why rewriting shared history causes trouble. See [merge vs rebase](https://levelcode.org/concepts/merge-vs-rebase).
- **Cheap comparison.** Two trees with the same hash are identical, so Git can skip whole directories when diffing or merging.

Branches and tags aren't objects at all. They're small files that name a commit hash. See [refs and HEAD](https://levelcode.org/concepts/git-refs).
