Lesson 1: The Git Mental Model — Snapshots, Not Diffs
Understand what Git actually is: a content-addressable filesystem that stores snapshots, not changes. Learn the three areas (working directory, staging, repository) and why this mental model makes everything else easy.
Lesson 1: The Git Mental Model — Snapshots, Not Diffs 🗂️
Introduction: Why Git Feels Confusing (And How to Fix It) 💡
If you've ever felt lost using Git, copy-pasting commands from Stack Overflow without understanding what they do, you're not alone. The problem isn't you — it's that most Git tutorials teach you commands before teaching you concepts.
Imagine learning to drive by memorizing "turn the wheel 37 degrees left, press pedal 2 inches" without understanding that the wheel controls direction and the pedal controls speed. That's how most people learn Git!
This lesson builds the mental model that makes Git intuitive. Once you understand how Git thinks, the commands will make perfect sense. 🧠
What Git Actually Is: A Content-Addressable Filesystem 📦
Let's start with the big reveal: Git is not primarily a version control system. At its core, Git is a content-addressable filesystem with a version control interface built on top.
What does "content-addressable" mean? It means Git stores data (files, directories, commits) and gives each piece a unique address based on its content. This address is a SHA-1 hash — a 40-character string like a3f5c9b2e8d1f6a7b4c3e2d1f0a9b8c7d6e5f4a3.
🤔 Did you know? The same file content always produces the same hash, no matter where or when you save it. This makes Git incredibly efficient — if two branches have the same file, Git only stores it once!
The Key Insight: Snapshots, Not Diffs 📸
Here's the fundamental concept that changes everything:
Git does not store changes (diffs). Git stores complete snapshots of your project.
Most other version control systems store the original file and then a series of changes:
Version 1: "Hello world"
Version 2: Change line 1 to "Hello Git"
Version 3: Add line 2 "Welcome"
Git doesn't work this way. Instead, it takes a complete snapshot of your entire project at each commit:
Commit 1: [Complete snapshot of all files]
Commit 2: [Complete snapshot of all files]
Commit 3: [Complete snapshot of all files]
💡 Efficiency tip: Don't worry about storage! If a file hasn't changed between commits, Git doesn't store it again — it just creates a link to the previous version. Git is incredibly space-efficient.
This snapshot model is why Git is so powerful for branching and merging. Each commit is a complete, self-contained snapshot, not dependent on a chain of changes.
The Three Areas: Where Your Code Lives 🏠
Git organizes your work into three distinct areas. Understanding these is crucial:
+------------------+ +------------------+ +------------------+
| WORKING | | STAGING | | REPOSITORY |
| DIRECTORY | ----> | AREA | ----> | (.git folder) |
| | git | (INDEX) | git | |
| Your actual | add | Prepared | commit| Permanent |
| files you edit | | snapshot | | history |
+------------------+ +------------------+ +------------------+
1. Working Directory (Working Tree) 🌳
This is your playground — the actual files and folders you see and edit. When you open a file in your text editor, you're working in the working directory.
Think of it as your drafting table where you make changes freely. Nothing here affects Git's history until you explicitly tell Git about it.
2. Staging Area (Index) 🎬
The staging area is Git's unique feature that confuses many beginners but becomes incredibly powerful once you understand it.
Think of the staging area as a photography studio where you arrange exactly what you want in your snapshot before taking the picture. You can:
- Add specific files: "I want this file in my next snapshot"
- Add parts of files: "I want these 3 lines but not those 5 lines"
- Review what you've staged before committing
The command git add moves changes from the working directory to the staging area.
💡 Why have a staging area? It lets you create clean, logical commits. You might have changed 10 files while working, but you can stage and commit related changes separately, making your history clear and meaningful.
3. Repository (.git folder) 🏛️
The repository is Git's permanent database — the .git folder in your project. This is where Git stores:
- All your commits (complete snapshots)
- All your branches
- All your history
- All the metadata
When you run git commit, Git takes everything from the staging area and creates a permanent snapshot in the repository.
⚠️ Important: Once something is committed to the repository, it's nearly impossible to lose. Even if you "delete" a commit, Git usually keeps it for weeks. This is why Git is so safe!
How Commits Actually Work 🔗
A commit is Git's fundamental unit. Let's demystify what a commit really is.
A commit contains:
- A complete snapshot of all staged files
- Metadata: author name, email, timestamp, commit message
- Parent commit(s): pointer(s) to the previous commit(s)
- A unique SHA-1 hash: the commit's address
+-----------------------------------+
| Commit: a3f5c9b |
|-----------------------------------|
| Author: Jane Developer |
| Date: 2024-01-15 10:30 |
| Message: "Add login feature" |
|-----------------------------------|
| Parent: f2e4d8a |
|-----------------------------------|
| Snapshot: |
| - index.html [hash: d3a2...]|
| - style.css [hash: b7f1...]|
| - app.js [hash: e8c4...]|
+-----------------------------------+
|
v
+-----------------------------------+
| Commit: f2e4d8a (parent) |
|-----------------------------------|
| Author: John Coder |
| Date: 2024-01-14 15:20 |
| Message: "Initial commit" |
+-----------------------------------+
Each commit points to its parent, creating a chain (actually a directed acyclic graph, but we'll get to that later). This chain is your project's history.
🧠 Mnemonic: Think of commits as linked snapshots — each one is complete but knows where it came from.
Detailed Examples: The Three Areas in Action 🎯
Example 1: Creating Your First Commit
Let's walk through creating a commit step by step, tracking what happens in each area:
Starting state:
Working Directory: (empty new project)
Staging Area: (empty)
Repository: (empty)
Step 1: Create a file
echo "Hello Git" > readme.txt
Working Directory: readme.txt (modified/new)
Staging Area: (empty)
Repository: (empty)
Git doesn't know about this file yet. It's only in your working directory.
Step 2: Stage the file
git add readme.txt
Working Directory: readme.txt
Staging Area: readme.txt (staged for commit)
Repository: (empty)
Git has prepared a snapshot that includes this file. The staging area now holds a copy.
Step 3: Commit the snapshot
git commit -m "Add readme file"
Working Directory: readme.txt
Staging Area: (clean - matches repository)
Repository: Commit a3f5c9b: "Add readme file"
↳ Contains: readme.txt
Git has created a permanent snapshot in the repository! The staging area is now clean because it matches what's in the repository.
Example 2: Multiple Changes, Selective Staging
This example shows why the staging area is powerful:
Starting state: You have a committed project with index.html and styles.css.
You make changes:
# Fix a bug in index.html
# Add a new feature in index.html
# Update colors in styles.css
Working Directory: index.html (modified)
styles.css (modified)
Staging Area: (clean)
Repository: Last commit
You want two separate commits (one for the bugfix, one for the feature + styling). You can stage selectively:
# Stage only the bugfix lines from index.html
git add -p index.html
# (select only the bugfix hunks)
git commit -m "Fix navigation bug"
Now commit 1 contains only the bugfix. Then:
# Stage the rest
git add index.html styles.css
git commit -m "Add color theme feature"
Now commit 2 contains the feature and styling changes. Your history is clean and logical! 🎉
Example 3: Understanding File States
A file in your project can be in one of several states:
+----------------+ +----------------+ +----------------+
| UNTRACKED | | UNMODIFIED | | MODIFIED |
| (new file, |---->| (committed, |---->| (changed since |
| git doesn't | git | hasn't been | edit| last commit) |
| know about it)| add | changed) | | |
+----------------+ +----------------+ +----------------+
| |
v v
+----------------+ +----------------+
| REMOVED | | STAGED |
| (deleted, | | (ready to be |
| staged for | | committed) |
| deletion) | | |
+----------------+ +----------------+
You can check file states with git status:
$ git status
On branch main
Changes to be committed: # STAGED
(use "git restore --staged <file>..." to unstage)
modified: index.html
Changes not staged for commit: # MODIFIED
(use "git add <file>..." to stage)
modified: styles.css
Untracked files: # UNTRACKED
(use "git add <file>..." to include)
new-feature.js
Example 4: The Commit Graph
Commits form a graph structure. Here's a simple linear history:
A <-- B <-- C <-- D (main)
^
HEAD
Each letter is a commit. Arrows point to parent commits. main is a branch (just a pointer to a commit). HEAD points to where you are now.
When you create a new commit:
git commit -m "Add new feature"
Git creates commit E:
A <-- B <-- C <-- D <-- E (main)
^
HEAD
The main branch moves forward automatically, and HEAD follows it.
💡 Key insight: Branches are just moveable pointers to commits. Creating a branch doesn't copy any files — it just creates a new pointer!
Why This Mental Model Makes Everything Easy 🚀
Once you understand that:
- Git stores snapshots, not changes
- Three areas exist (working, staging, repository)
- Commits are linked snapshots with metadata
- Branches are just pointers to commits
...then all Git commands become logical:
git add= "Move this to staging"git commit= "Create a snapshot from staging"git checkout= "Move HEAD to a different commit"git branch= "Create a new pointer"git merge= "Combine two commit histories"git rebase= "Replay commits on a different base"
No more memorizing! Each command is just manipulating these fundamental structures.
Common Mistakes ⚠️
Mistake 1: Thinking Git Stores Diffs
❌ Wrong thinking: "Git saves the changes I made to each file."
✅ Correct thinking: "Git saves complete snapshots. It can show me diffs by comparing snapshots."
Why it matters: This misconception makes branching and merging seem scary. If you think Git stores diffs, you imagine complex chains of changes that might conflict. But since Git stores complete snapshots, branches are just different snapshot sequences!
Mistake 2: Confusing Working Directory and Repository
❌ Wrong: "If I delete a file, it's gone from Git forever."
✅ Correct: "If I delete a file from my working directory, it's still in the repository until I commit that deletion."
Example:
rm important.txt # Deleted from working directory only!
git status # Shows "deleted: important.txt"
git restore important.txt # Brings it back from repository!
The file is safe in the repository until you git add the deletion and commit it.
Mistake 3: Not Using the Staging Area
❌ Wrong pattern:
git add .
git commit -m "Made changes"
This stages everything indiscriminately, leading to messy commits that mix unrelated changes.
✅ Better pattern:
git add file1.js file2.js # Only related changes
git commit -m "Add user authentication"
git add file3.css
git commit -m "Update button styles"
Each commit has a clear, single purpose.
Mistake 4: Fearing Commits
❌ Wrong: "I'll wait until everything is perfect before committing."
✅ Correct: "I'll commit small, logical units of work frequently."
Why: Frequent commits give you more save points to return to. If you break something, you can easily go back. Commits are cheap — Git is designed for many small commits, not few giant ones.
Mistake 5: Not Understanding HEAD
❌ Wrong: "HEAD is magical and confusing."
✅ Correct: "HEAD is just a pointer showing where I am right now (usually pointing to a branch, which points to a commit)."
HEAD --> main --> commit D
When you checkout a different branch, HEAD moves:
HEAD --> feature --> commit F
That's it! No magic.
Key Takeaways 🎓
Git is a content-addressable filesystem that stores data by content hash
Git stores snapshots, not diffs — each commit is a complete picture of your project
Three areas exist:
- Working Directory: Your actual files (the drafting table)
- Staging Area: What you're preparing to commit (the photography studio)
- Repository: Permanent history (the archive)
Commits are linked snapshots containing:
- Complete file snapshot
- Metadata (author, date, message)
- Parent commit pointer(s)
- Unique SHA-1 hash
The staging area lets you craft clean, logical commits by selecting exactly what to snapshot
Branches are just moveable pointers to commits — cheap and easy to create
HEAD is a pointer showing where you are (usually points to a branch)
This mental model makes all Git commands logical — they're just manipulating these structures
🔧 Try this: After this lesson, run git status in one of your projects. You'll now understand exactly what each section means: which area each file is in, and what Git is telling you about the state of your project!
📚 Further Study
Git Internals - Plumbing and Porcelain (Official Git Book): https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain Deep dive into how Git stores objects and creates snapshots
A Visual Git Reference: https://marklodato.github.io/visual-git-guide/index-en.html Excellent diagrams showing how Git commands affect the three areas
Git from the Bottom Up: https://jwiegley.github.io/git-from-the-bottom-up/ Builds Git understanding from the object database up
📋 Quick Reference Card
+------------------------------------------------------------------+
| GIT MENTAL MODEL CHEAT SHEET |
+------------------------------------------------------------------+
| CORE CONCEPT |
| Git stores SNAPSHOTS, not changes |
| Each commit = complete picture of your project |
+------------------------------------------------------------------+
| THREE AREAS |
| Working Directory → Staging Area → Repository |
| (your files) (prepared) (permanent) |
+------------------------------------------------------------------+
| KEY COMMANDS |
| git add <file> Move working → staging |
| git commit -m "..." Move staging → repository (create snapshot)|
| git status See state of all three areas |
| git log View commit history |
+------------------------------------------------------------------+
| WHAT IS A COMMIT? |
| ✓ Complete snapshot of all files |
| ✓ Author, date, message |
| ✓ Parent commit pointer(s) |
| ✓ Unique SHA-1 hash (address) |
+------------------------------------------------------------------+
| FILE STATES |
| Untracked → git doesn't know about it |
| Unmodified → committed, hasn't changed |
| Modified → changed since last commit |
| Staged → ready to be committed |
+------------------------------------------------------------------+
| REMEMBER |
| • Commits are CHEAP → commit often! |
| • Staging area = craft logical commits |
| • Branches = just pointers to commits |
| • HEAD = where you are now |
+------------------------------------------------------------------+
You now have the foundational mental model that makes Git make sense! In the next lessons, we'll build on this foundation to master branching, merging, rebasing, and fixing mistakes — all of which will be easy now that you understand how Git really works. 🚀