The Git Object Model
At its core, Git is a content-addressable filesystem sitting underneath a version control interface. Everything Git tracks — file contents, directory structure, commit history, and tags — is stored as one of four object types inside the .git/objects database: blobs, trees, commits, and tags. Each object is identified by the SHA-1 (or, in newer repositories, SHA-256) hash of its own content, meaning the object's name is derived entirely from what it contains. This is what makes Git 'content-addressable': two files with identical content anywhere in history, in any branch, produce the exact same hash and are stored only once.
Cricket analogy: A stadium's video archive stores every camera angle by a fingerprint of its actual footage content rather than by match name; if two different matches happen to share byte-identical stock replay graphics, it's stored only once and both matches point to it.
Blobs: File Contents
A blob (binary large object) stores the raw contents of a single file — and nothing else. It has no filename, no permissions, no directory location; that metadata lives one level up, in a tree object. Because a blob is identified purely by a hash of its content, if you have ten files across your project with byte-identical content, Git stores that content exactly once and points ten different tree entries at the same blob.
Cricket analogy: A video archive's raw footage file has no label attached at all, just pure footage; match name, date, and camera angle live in a separate index sheet, so if ten matches used identical byte-for-byte crowd-noise clips, the archive stores it once and ten entries point to it.
Trees: Directory Structure
A tree object represents a directory. It lists entries, each pairing a filename, a file mode (permissions, and whether it's a regular file, executable, or symlink), and a pointer to either a blob (for a file) or another tree (for a subdirectory). Trees can nest arbitrarily deep, mirroring your actual directory structure. A commit's snapshot is really just a pointer to one root tree object, which recursively references every file and folder in the project at that moment.
Cricket analogy: A squad sheet lists each player's name, role, and a pointer to their performance file, with sub-sheets nesting within it for batting order and bowling attack; the whole match scorecard is really just one pointer to that season's root squad sheet.
Commits: Snapshots with Context
A commit object ties everything together. It points to exactly one root tree (the snapshot), records author and committer name/email/timestamp, includes the commit message, and — critically — points to one or more parent commits (zero parents for the very first commit, two or more for a merge commit). This parent pointer is what forms the commit graph, the directed acyclic structure that git log walks to show you history. Because a commit's hash is computed from its tree, parents, and metadata, changing anything about a commit (even just its message) produces an entirely new hash — this is why 'editing history' in Git always means creating new commit objects, not mutating old ones.
Cricket analogy: A match report ties everything together — it points to the final scorecard, records the umpires' names and date, includes a summary, and references the previous match as its parent (two parents for a merged reserve-day result); changing even the toss result produces an entirely new report, never overwriting the old one.
# Inspect the object type and content of any hash Git knows about
git cat-file -t a1b2c3d # e.g. "commit"
git cat-file -p a1b2c3d # pretty-print the object's content
# A commit object's pretty-printed content looks like:
# tree 8f3d2e1a9b7c6f5e4d3c2b1a0f9e8d7c6b5a4938
# parent 7c6b5a4938f3d2e1a9b7c6f5e4d3c2b1a0f9e8d7
# author Ada Lovelace <ada@example.com> 1751020800 +0000
# committer Ada Lovelace <ada@example.com> 1751020800 +0000
#
# Add retry logic to the payment webhook handler
# Walk the tree of a commit to see files and subdirectories
git ls-tree -r HEADEvery branch you see in Git (main, feature/login, etc.) is nothing more than a lightweight, movable pointer — a 41-byte text file — referencing a single commit hash. This is why creating a branch in Git is nearly instantaneous: it does not copy any files, it just writes a new pointer.
Tags: Named References
A lightweight tag is simply a named pointer to a commit, similar to a branch but one that (by convention) does not move. An annotated tag is a distinct fourth object type: it has its own hash and stores a tagger, date, message, and optional GPG signature, alongside a pointer to the commit it tags. Annotated tags are preferred for marking releases because they carry this extra metadata and can be cryptographically signed to prove authenticity.
Cricket analogy: A sticky-note bookmark on a scorecard page just marks a spot, while an official certificate for a record-breaking innings carries the umpire's name, date, and citation, and can be authenticated — that certificate goes on the Hall of Fame plaque, not the sticky note.
- Git's object database has four object types: blobs (file content), trees (directory structure), commits (snapshots with history), and tags (named, optionally signed pointers).
- Every object is identified by a hash of its own content, making Git a content-addressable store that automatically deduplicates identical data.
- A commit points to one root tree plus zero or more parent commits, and this parent chain forms the commit graph.
- Blobs store only content — filenames and permissions live in tree entries, not in the blob itself.
- Branches and lightweight tags are just movable (or fixed) pointers to a commit hash, not copies of data.
- git cat-file lets you inspect the type and raw content of any object directly, which is invaluable for understanding Git internals.
Practice what you learned
1. What does a Git blob object store?
2. How does a commit object reference the state of the project at that point in history?
3. Why is Git described as a 'content-addressable' filesystem?
4. What is a Git branch, structurally?
5. What distinguishes an annotated tag from a lightweight tag?
Was this page helpful?
You May Also Like
What Is Git?
An overview of Git as a distributed version control system, its origin, design goals, and the core concepts that distinguish it from other tools.
The Three States of Git
Understand Git's core mental model — working directory, staging area, and repository — and how files move between them during a typical edit-commit cycle.
Committing Changes
How to create commits, write effective commit messages, and understand what a commit actually records in Git's history.
Git Tags and Releases
Understand how git tags mark specific commits as meaningful milestones like versions, and how lightweight vs annotated tags support release workflows and reproducible deployments.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringDesign Patterns Study Notes
Software Design · 30 topics
Software EngineeringSoftware Engineering Study Notes
Python · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics