Most git tutorials hand you a list of commands and tell you to memorize them. git add, git commit, git push — three commands you'll type a thousand times. But the moment something goes wrong — a merge conflict, a detached HEAD, a "why is my branch missing" — those tutorials leave you stranded.

This post is different. Instead of giving you another command cheat-sheet, it walks through the five concepts that make every git command obvious. Once these click, you'll never need to look up basic commands again — and the scary commands (rebase, reset, cherry-pick) stop being scary.

Quick Answer

Git is built on five ideas: there are three areas for your changes (working, staging, repository), commits are full snapshots (not diffs), branches are just pointers to commits, merge and rebase are different ways to combine branches, and remote and local are independent copies. Every git command moves things between these. Once you see the picture, the commands name themselves.

Concept 1: The Three Areas

Every git command operates on one or more of three places where your code can live:

THE THREE AREAS Working dir Staging area Repository (your files) (.git/index) (.git/objects) ┌─────────┐ ┌──────────┐ ┌──────────┐ │ edit │ →→ │ git add │ →→ │ git │ │ files │ │ "ready" │ │ commit │ │ here │ │ to save │ │ (saved) │ └─────────┘ └──────────┘ └──────────┘ ↑ ↑ ↑ └─ git restore ┘ │ └────── git restore --staged ───┘

Working directory is where you edit files. Staging area (also called the index) is a "ready to commit" buffer — you put changes here when they're polished. Repository is the permanent record — once something's committed, it's saved forever.

Every git command makes more sense once you know which area it touches:

git add file.txtworking → staging
git commit -m "..."staging → repository
git restore file.txtdiscard changes in working
git restore --staged file.txtunstage (staging → working)
git statusshow what's in each area
git diffworking vs staging
git diff --stagedstaging vs last commit

This is why git status shows two sections — "Changes to be committed" (staging) and "Changes not staged for commit" (working). They're literally different buckets.

Concept 2: Commits Are Snapshots, Not Diffs

Most beginners think a commit stores "the changes I made". Wrong. A commit stores the entire state of your project at that moment. It's a photograph of every file, not a list of edits.

EACH COMMIT = A FULL SNAPSHOT Commit A Commit B Commit C ─────── ─────── ─────── index.html index.html index.html (modified) style.css style.css →→ style.css app.js (new) app.js logo.png (new)

Git is smart about storage — files that don't change are stored once and referenced from multiple commits. But conceptually, every commit holds the full project. That's why you can git checkout any commit from years ago and instantly see exactly what the code looked like then.

This explains a few things:

Concept 3: Branches Are Just Pointers

People imagine branches as parallel lines or copies of all the files. They're not. A branch is a 41-byte text file that holds the ID of one commit — that's it.

BRANCHES POINT AT COMMITS A ←── B ←── C ←── D (main) ↑ HEAD Run: git branch feature A ←── B ←── C ←── D (main, feature) ↑ HEAD Run: git checkout feature; (commit) E A ←── B ←── C ←── D (main) ↘ E (feature) ↑ HEAD

Creating a branch is instant — git just writes a new file containing the current commit's ID. Switching branches doesn't copy anything; git updates the working directory to match the snapshot of the target branch's commit.

HEAD is also a pointer — usually pointing at a branch (which points at a commit). When you commit, the branch you're on advances to the new commit, and HEAD follows along.

This is why:

Concept 4: Merge vs Rebase, Visually

Both combine work from one branch into another. They produce different histories.

Before either: you have main and feature, diverged.

A ←── B ←── C (main) ↘ D ←── E (feature)

git merge feature (while on main) — creates a new merge commit M that has two parents:

AFTER MERGE A ←── B ←── C ────── M (main) ↘ ↗ D ←── E ──╯ (feature)

The history shows the branch shape clearly. M's commit message is "Merge branch feature".

git rebase main (while on feature) — replays D and E on top of C, creating new commits D' and E':

AFTER REBASE A ←── B ←── C ←── D' ←── E' (main, feature)

Linear history. No merge commit. But — the commit IDs of D and E changed (they became D' and E'). If you'd already pushed those commits, others now have orphaned versions.

The rule:

Concept 5: Local vs Remote (origin/main is not main)

When you clone a repo, git creates two parallel sets of branches: yours (local) and tracking copies of the server's (remote-tracking).

YOUR LAPTOP Local branches: Remote-tracking branches: main ←──────→ origin/main feature ←──────→ origin/feature experiment Commands that move data: git fetch → pull updates from server into origin/* (no merge) git pull → fetch + merge origin/main into main git push → send local main to server (which updates origin/main)

origin/main is git's idea of what the server's main looks like, last time you talked to the server. It's a snapshot, not a live connection. Run git fetch to refresh it.

This explains:

The 7 Commands You'll Actually Use 95% of the Time

Forget the 100-command lists. These seven cover almost everything:

git statusWhat's in each area, what branch you're on
git add <files>Stage changes ready to commit
git commit -m "msg"Save staged changes as a snapshot
git pushUpload your commits to the remote
git pullDownload + merge remote changes
git checkout -b featureCreate and switch to a new branch
git log --oneline --graph --allSee the history visually

The "advanced" commands you'll need eventually:

git restore <file>Discard a working file's changes
git restore --staged <file>Unstage a file (keep changes)
git reset HEAD~1Undo last commit (keep changes)
git revert <commit>Undo a pushed commit safely
git stash / git stash popTemporarily shelve changes
git rebase mainReplay your commits on top of main
git cherry-pick <commit>Apply a single commit from elsewhere

Learn Git hands-on

Our Git course walks through every concept above with interactive practice — branching, merging, conflicts, and recovery.

Start Git Course

Frequently Asked Questions

What is the difference between git add and git commit?

git add moves changes from your working directory to the staging area — it marks them ready to be committed. git commit takes everything currently in the staging area and saves it as a permanent snapshot in the repository. Two-step design lets you choose exactly what goes in each commit.

What is the difference between merge and rebase in git?

Merge combines two branches by creating a new merge commit that joins them — preserves the full history with the branch shape visible. Rebase replays your branch's commits on top of another branch — produces a linear history but rewrites commit IDs. Use merge for shared branches, rebase for cleaning up your local work before pushing.

Are git branches actual copies of files?

No. A branch in git is just a pointer (a 41-byte text file) holding the commit ID it points to. Creating a new branch is instant because nothing is copied. Switching branches just moves the HEAD pointer and updates your working directory to match that commit's snapshot.

How do I undo my last git commit?

git reset --soft HEAD~1 undoes the commit but keeps your changes staged. git reset HEAD~1 (default --mixed) keeps the changes in the working directory but unstaged. git reset --hard HEAD~1 throws away the changes entirely. If the commit was already pushed, use git revert instead — it creates a new commit that reverses the changes.

What does HEAD mean in git?

HEAD is a pointer to the commit you're currently looking at — usually the tip of the branch you have checked out. When you make a new commit, HEAD moves to that new commit. When you switch branches, HEAD moves to point at that branch. "Detached HEAD" means HEAD points directly to a commit instead of a branch — fine for inspecting, dangerous for committing.

Related Reading

The Bottom Line

Git stops being scary the moment you stop seeing it as a list of commands and start seeing the picture: three areas, snapshots, pointers, two ways to combine, and a local-remote split. Every command you'll ever run moves something between these.

The next time something feels weird — a merge conflict, a missing file, a "detached HEAD" warning — pause and ask: which area is this about? Where are the pointers? What snapshot is HEAD on? With those questions, the answer almost always reveals itself.

Skip the cheat-sheet. Internalize the model. The commands will follow.