Commits, objects, and the three working areas — the foundation everything else builds on
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.
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.
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.
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.
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.