100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

The Git Object Model

A deep dive into how Git stores data internally as blobs, trees, commits, and tags — the content-addressable database underlying every Git operation.

Version Control FoundationsIntermediate10 min readJul 9, 2026
Analogies

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.

bash
# 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 HEAD

Every 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

Was this page helpful?

Topics covered

#Bash#GitVersionControlStudyNotes#SoftwareEngineering#TheGitObjectModel#Git#Object#Model#Blobs#OOP#StudyNotes#SkillVeris