This lesson on Git's Mental Model — Commits, Trees, Refs is hands-on and example-driven. You will be able to diagnose and troubleshoot complex Git issues by moving beyond surface-level commands and understanding the underlying data structure. You will learn how Git uses unique SHA-1 hashes to link file content, directory structure, and historical metadata into a reliable, traceable project lineage.
What You'll Be Able To Do
- Differentiate the roles of Blob, Tree, and Commit objects in forming a repository snapshot.
- Trace the lineage of a project snapshot using parent pointers within the Directed Acyclic Graph (DAG).
- Utilize SHA-1 hashes for precise referencing during advanced operations like resetting or rebasing.
- Inspect the raw contents of any Git object using the command line inspection tool.
- Explain how content addressability ensures data integrity and enables storage optimization.
Detailed Concept Walkthrough
1. SHA-1 Hashing & Content Addressability
Git uses the SHA-1 cryptographic hash function to generate a unique 40-character identifier for every object it stores. This hash is derived entirely from the object's contents, making Git a content-addressable file system.
- Mechanism: When Git stores a Blob, Tree, or Commit, it calculates the SHA-1 hash of that object's exact content plus a header defining its type and size. The resulting hash is the object's unique, immutable name.
- Under the Hood: Because the hash depends only on the content, identical content (even if stored in different locations or times) will always generate the exact same SHA-1 hash, enabling efficient storage deduplication.
- Best Practice: When referencing an object via its SHA, you only need to provide a short, non-ambiguous prefix (typically 7 or 8 characters) rather than the full 40-character string, simplifying command line usage.
# 1. Create a file and stage it (creating a Blob object)
echo "Initial content" > file.txt
git add file.txt
# 2. Find the SHA of the staged Blob
# The output format is: mode SHA stage_number filename
git ls-files -s
# 3. Inspect the raw content of the Blob using its SHA (e.g., 8a3c4d5f)
git cat-file -p 8a3c4d5f
Key Takeaway: The SHA-1 hash is the immutable, unique identifier for the object, guaranteeing that the content you retrieve is exactly what was stored.
2. Commit, Tree, and Blob Hierarchy
Git stores project history as a series of snapshots, where each snapshot is defined by a Commit object that points to a Tree object, which recursively points to other Trees and Blobs. This hierarchy separates file content from directory structure and history metadata.
- Mechanism: A Blob object holds the raw file content, while a Tree object represents a directory, containing pointers (SHA hashes) to Blobs (files) and other Trees (subdirectories), along with their names and permissions.
- Under the Hood: The Commit object is the primary historical record; it contains metadata (author, date, message), a pointer to the top-level Tree object defining the project state, and pointers to its parent commit(s).
- Execution Flow: When you run
git commit, Git sequentially creates Blobs for changed files, then creates Trees reflecting the structure, and finally creates the Commit object linking the new content and history together. - Best Practice: Always remember that a branch or tag is not a copy of code; it is merely a lightweight, human-readable pointer (a reference) that points directly to a specific, immutable Commit SHA.
# Inspecting a Commit object (using HEAD as the reference)
git cat-file -p HEAD
# Output structure:
# tree 8a3c4d5f... (SHA of the top-level Tree for this snapshot)
# parent 1b2e3f4g... (SHA of the previous Commit)
# author User <user@example.com> 1678886400 -0500
#
# Commit message goes here
Key Takeaway: The Commit object links the project's content (via the Tree) with its history (via the Parent pointer).
3. Directed Acyclic Graph (DAG) Structure
Git history is structured as a Directed Acyclic Graph (DAG), where commits are nodes that point backward to their parent nodes, forming a non-circular, traceable lineage. This structure is fundamental to Git's ability to manage complex branching and merging.
- Mechanism: Every standard commit has exactly one parent pointer, except for the initial commit (zero parents) and merge commits (two or more parents). This parent pointer defines the direction of history flow, always moving backward in time.
- System Behavior: The 'Acyclic' property ensures that history is linearizable and prevents infinite loops; you can never follow parent pointers and return to the starting commit, guaranteeing a clear timeline.
- Under the Hood: References like
masterandHEADare special, moving pointers that indicate the tip of a line of work or the current working commit, respectively. Committing simply creates a new object and updates the branch pointer to reference it. - Best Practice: Use
git log --graphto visualize the DAG structure, which is essential for understanding complex operations like rebasing or diagnosing history divergence during a merge conflict.
# Visualize the DAG structure of the history, showing branches and merges
git log --oneline --graph --all
# Example of inspecting the parent pointer of the current commit
git cat-file -p HEAD | grep parent
# Example of inspecting the parent's parent
git cat-file -p HEAD^
Key Takeaway: Git history is a chain of commits pointing backward, and branches are just lightweight labels pointing to the latest link in that chain.
Topics Covered in Git's Mental Model — Commits, Trees, Refs
- Standard Git Workflow (00:00 - 01:25) — The standard Git lifecycle involves moving changes from the working area to the local repository.
- Demystifying SHA Hash (01:27 - 03:15) — SHA-1 strings are unique identifiers visible in the UI that represent the fundamental unit of Git reference.
- Core Git Object Model (03:22 - 05:49) — Git stores snapshots using three object types: Blob (content), Tree (structure), and Commit (metadata/history).
- Git as a Database (05:51 - 07:22) — Git history forms a Directed Acyclic Graph (DAG) where commits point to their parents, ensuring traceable lineage.
- Content Addressability (07:25 - 08:42) — Every object's SHA is generated purely from its contents, guaranteeing uniqueness and enabling deduplication.
- Practical Inspection (08:43 - 10:15) — The git cat-file -p command allows users to view the raw contents of any Git object using its SHA hash.
Git Cheat Sheet
-
SHA-1 Hash— Unique 40-character identifier for any Git object- In practice: 8a3c4d5f...
-
git cat-file -p <SHA>— Inspects raw contents of any Git objectgit cat-file -p HEAD^{tree} -
Blob Object— Stores the exact content of a file -
Tree Object— Stores directory structure and pointerstree 8a3c4d5f file.txt -
Commit Object— Stores metadata, parent pointer, and top-level Tree SHAparent 1b2e3f4g -
HEAD— Moving pointer indicating the current working commitgit show HEAD
Comparison Table
| Object Type | Role | Content Stored | Mutability |
|---|---|---|---|
| Blob | File Content | Raw file data only. | Immutable |
| Tree | Directory Structure | Pointers to Blobs and Trees. | Immutable |
| Commit | History Snapshot | Metadata, Parent SHA, Top Tree SHA. | Immutable |
| Reference (Branch) | Human Label | Pointer to a specific Commit SHA. | Mutable (Moves) |
Common Pitfalls
- Mistake: Believing a branch is a copy of the code. Avoid: Recognizing a branch is only a moving pointer to a Commit SHA.
- Mistake: Assuming identical files stored in different directories have different SHAs. Avoid: Understanding that content addressability means identical content yields identical Blobs.
- Mistake: Trying to modify an existing Commit object. Avoid: Knowing that commits are immutable; any change creates a brand new Commit object.
- Mistake: Confusing the Tree SHA with the Commit SHA.
Avoid: Using
git cat-file -p <Commit SHA>to see the Tree SHA it references.
FAQs
- What happens when I delete a branch? You are only deleting the lightweight pointer (the reference); the actual Commit objects remain in the repository until garbage collection runs.
- Why does Git use SHA-1 if it's considered cryptographically weak now? SHA-1 is used primarily for content integrity and identification, not security; the risk of an accidental collision in a single repository is negligible.
- How does Git ensure data integrity if I only use a short SHA prefix? Git only accepts the short prefix if it is non-ambiguous; if multiple objects share that prefix, Git will throw an error and require a longer SHA.