Git Core Model

Commits, objects, and the three working areas — the foundation everything else builds on

Why

Most git confusion — why merge creates an extra commit, why rebase changes SHAs, why git reset does different things with different flags — disappears once you understand what git is actually storing. Git is a content-addressable key-value database of immutable objects. Commits don't store diffs; they store snapshots. This distinction drives every other behaviour.

Explaining what actually happens when you run git commit separates engineers who understand git from those who have only memorised commands.

Intuition

Git stores four object types, each identified by its SHA-1 hash: blob (file content), tree (directory listing of blobs and subtrees), commit (snapshot + parent pointer + message), and tag (named pointer). The same file in two commits produces the same blob hash — git never duplicates content.

You work across three areas: the working directory (files on disk), the staging area / index (preview of your next commit), and the repository (the object database inside .git/). Every git command moves content between these three areas.

Explanation
The three areas
# Working directory → Staging area (index) git add file.py # stage a specific file git add -p # interactively stage individual hunks # Staging area → Repository git commit -m "message" # snapshot staged content into a commit object # Inspect each area git status # working dir vs staging vs HEAD git diff # working dir vs staging (unstaged changes) git diff --staged # staging vs HEAD (what will be committed) # Restore working dir from HEAD (discard local edits) git restore file.py # Git 2.23+; older: git checkout -- file.py

Understanding which area each command touches prevents most "where did my change go?" moments. git add copies content to the index — the working directory still has your edits. git commit reads the index, not the working directory directly.

What a commit actually is
# A commit object contains: # pointer to root tree (full snapshot of all files) # parent commit SHA(s) (none for the first commit; two for a merge) # author, committer, timestamps # commit message # Inspect raw objects git cat-file -p HEAD # show the commit object git cat-file -p HEAD^{tree} # show the tree it points to # Visualise the commit graph git log --oneline --graph --all

Commits are immutable. When you amend or rebase, git creates new commit objects with new SHAs. The old commits still exist in the object database until garbage-collected — which is why git reflog can recover them.

Undoing with git reset
# git reset moves the branch pointer (and HEAD) to the target commit. # The flag controls what happens to staging and working directory: git reset --soft HEAD~1 # move HEAD back; staging + working dir unchanged # → changes remain staged, ready to re-commit git reset --mixed HEAD~1 # (default) move HEAD + unstage; working dir intact # → changes on disk, not staged git reset --hard HEAD~1 # move HEAD + unstage + discard working dir changes # → edits are gone (recoverable via reflog ~30 days)

Soft is safest — "I committed too early." Hard is destructive — use only when you truly want to throw away the changes. All three are recoverable via git reflog as long as the commits haven't been garbage-collected.