# Git, Explained Like You're a Human

> What Git actually is, what every everyday command really does, and how to stay calm when it breaks.


---

# Git, Explained Like You're a Human

You already *use* Git. You `add`, `commit`, `push`. And yet when something goes sideways, your
stomach drops - because nobody ever told you what Git is *actually doing*. This guide fixes that.

By the end, branches, HEAD, staging, and merge conflicts won't be scary words - they'll make sense,
because you'll understand the handful of simple ideas underneath all of them.

## How to read this
- **In a panic right now?** Jump to [Phase 3: When it breaks](03-when-it-breaks.md) and use the
  cheat-card at the top.
- **Want it to finally make sense?** Read in order - each phase builds on the last.

## The three phases
1. **[The Mental Model](01-the-mental-model.md)** - what Git *actually is*. Five ideas that make
   everything else click.
2. **[The Everyday Commands](02-everyday-commands.md)** - the commands you use daily, what each one
   *really* does, with real examples.
3. **[When It Breaks](03-when-it-breaks.md)** - the common "oh no" moments and how to fix them calmly.

> Deep disaster recovery (recovering lost commits with the reflog, undoing *already-pushed* history,
> rescuing a botched rebase) is its own guide - coming next.


---

# The Mental Model - What Git Actually Is

You've used Git. You've typed `git commit`, `git push`, maybe `git pull` a hundred times. And yet the
moment something unexpected happens - a merge conflict, a "detached HEAD," a teammate's force-push -
your stomach drops.

Here's the secret nobody tells you: **almost every Git nightmare is the same problem wearing different
masks - not understanding what Git is actually doing underneath.** Git isn't terrifying or arbitrary; it's
built on about five simple ideas. Once they click, the fear doesn't shrink - it disappears, because you can
*reason* about what's happening instead of guessing.

So we won't memorize commands yet. First, the five ideas. Give me twenty minutes and Git stops being a
haunted house.

## Idea 1: A commit is a snapshot

**What it actually is.** A commit is a photograph of your *entire project* at one moment in time - not
"the changes you made," the whole thing, every file, exactly as it looked when you hit commit. Git names
that photograph a unique hash (like `9f2a1c7`) and remembers its *parent*: the commit right before it.

**Why people get this wrong.** Most tutorials say a commit "stores your changes," so people picture Git
stacking diffs. That breaks the first time you try to undo something - a commit is a complete snapshot,
not a diff, and that's what makes everything else make sense.

```mermaid
gitGraph
  commit id: "C1"
  commit id: "C2"
  commit id: "C3"
```
Each box is a full snapshot pointing back at its parent - walk the arrows backward to see history.

**A real example.**
```console
$ git commit -m "Add login button"
[main 9f2a1c7] Add login button
 1 file changed, 12 insertions(+)
```
*What just happened:* Git snapshotted your project, named it `9f2a1c7`, and recorded its parent - the
commit you were on a moment ago. The `1 file changed` summary is Git being friendly; what it actually
stored is the full snapshot, not only those 12 lines.

**Why this saves you later.** Old versions aren't destroyed when you make new ones - commits only ever
point backward. "I think I lost my work" is almost always wrong: the snapshot is still there, you only
lost sight of it.

## Idea 2: A branch is a sticky note

**What it actually is.** Here's the idea that unlocks everything: **a branch is a sticky note with a
name on it, stuck onto one specific commit.** `main` isn't a copy of your project or a folder - it's a
label that says "this commit is the tip of `main`."

```mermaid
gitGraph
  commit id: "C1"
  commit id: "C2"
  commit id: "C3"
```
`main` sits on C3, the newest commit - a sticky note, not a copy.

**What it does in real life.** When you commit on `main`, Git creates the snapshot, then *peels the
sticky note off the old commit and sticks it on the new one*. The label always rides the newest commit on
that branch.

```mermaid
gitGraph
  commit id: "C1"
  commit id: "C2"
  commit id: "C3"
  commit id: "C4"
```
Commit C4, and Git peels `main` off C3 onto C4 - the label always rides the newest commit.

**A real example.** Creating a branch costs almost nothing - you're just adding a second sticky note:
```console
$ git branch feature
$ git log --oneline -1
9f2a1c7 (HEAD -> main, feature) Add login button
```
*What just happened:* `git branch feature` put a second label on the *exact same commit* `main` is on -
nothing was copied. The log shows both `main` and `feature` on `9f2a1c7`. That's why branching in Git is
instant: it's a line of bookkeeping, not a duplicate of your code.

**Why this saves you later.** Once you see branches as movable labels, scary things turn simple: "undo a
commit" means move the label back; "committed to the wrong branch" means put a label on it, then move the
wrong label back; "where did my branch go" means the commits are fine, a label moved. Hold onto this
one - it's the most valuable idea in Git.

## Idea 3: HEAD is "you are here"

**What it actually is.** `HEAD` is the "you are here" arrow - it points at the branch you're on, and
therefore at the commit you're sitting on.

```mermaid
flowchart TD
  HEAD(HEAD) --> main(main)
  main --> C3
  C1 --> C2 --> C3
```
Read it as: "you are on `main`, which is currently at commit C3."

**What it does in real life.** Switching branches moves the arrow:
```console
$ git switch feature
Switched to branch 'feature'
```
*What just happened:* HEAD now points at `feature` instead of `main`, and your files on disk changed to
match `feature`'s commit. You didn't move any commits - you moved *yourself*.

**The gotcha: "detached HEAD."** Sometimes HEAD points *directly at a commit* instead of a branch label -
it sounds like a horror-movie injury but means something tame: "you're here, but not on any branch." It
happens when you check out a commit by its hash. Look around safely, but create a branch before committing
anything you want to keep - otherwise no label is following you and those commits get hard to find later.

**Why this saves you later.** "Detached HEAD" is one of the most common Git panics, and it's harmless -
just the "you are here" arrow standing on a commit with no sticky note. The fix: `git switch -c my-branch`.

## Idea 4: The three places your work lives

**What it actually is.** Between "I edited a file" and "it's saved in history," your work passes through
*three* places. This is the source of more confusion than anything else in Git, and it's genuinely
simple once you draw it:

```mermaid
flowchart LR
  W(working directory) -->|add| S(staging area)
  S -->|commit| R(repository)
```

1. **Working directory** - your actual files, with your actual edits. What you see in your editor.
2. **Staging area** (also called the *index*) - a box where you place the changes you want in your
   *next* commit. Picture packing a box before you tape it shut.
3. **Repository** - the committed history; the snapshots that are now permanent.

**What it does in real life.** `git add` copies the current file into the box; `git commit` tapes the box
shut into a snapshot.

```console
$ git add file.js
$ git status
On branch main
Changes to be committed:
  modified:   file.js
```
*What just happened:* `file.js` is now *in the box* ("changes to be committed"). It isn't in history
yet - you haven't committed - but you've decided it's going in the next snapshot.

**The gotcha that bites everyone.** Staging holds the file *as it was the moment you ran `git add`*. Edit
it again afterward and those newer edits aren't in the box - Git will show the same file as both "staged"
and "not staged":
```console
$ git status
Changes to be committed:
  modified:   file.js        (the version you added)
Changes not staged for commit:
  modified:   file.js        (the edits you made AFTER adding)
```
*What just happened:* Both lines are true - the box holds the older version, your working file has newer
edits on top. Run `git add file.js` again to update the box.

**Why this saves you later.** "Why isn't my change in the commit?" and "why is `git diff` empty?" are
both this. Staging is a real place, separate from your files and separate from history - know there are
three places and you always know which one to check.

## Idea 5: The remote is another copy

**What it actually is.** A *remote* (the famous `origin`) is another complete copy of your repository
that lives somewhere else - on GitHub, GitLab, a company server. It isn't special. It isn't "the real
one." It's a peer copy that you sync with.

```mermaid
flowchart LR
  L(your computer) -->|push| O(origin)
  O -->|fetch / pull| L
```

**What it does in real life.** Three sync operations move commits between the copies:
- **`git push`** - "send my new commits up to origin."
- **`git fetch`** - "download origin's new commits so I can see them," without touching your files.
- **`git pull`** - "fetch, then merge them into what I'm working on" (fetch + apply, in one step).

**The gotcha.** Because both copies move independently, they drift apart. If origin has commits you
don't have yet and you try to push, Git refuses - it won't overwrite history you haven't seen:
```console
$ git push
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'origin'
```
*What just happened:* Someone pushed to origin before you, so the two copies disagree about where `main`
should point and Git won't guess. Pull first (bring in their commits), then push - a sync conflict, not a
catastrophe.

**Why this saves you later.** Much of "Git won't let me push!" terror is just two copies out of sync, and
Git refusing to silently clobber one with the other.

## The five ideas, recapped

That's the whole foundation. Read these five lines slowly:

1. **A commit** is a complete snapshot of your project, with a name and a pointer to its parent.
2. **A branch** is a movable sticky note pointing at one commit.
3. **HEAD** is the "you are here" arrow - usually pointing at your current branch.
4. **Three places**: your files → the staging box (`add`) → committed history (`commit`).
5. **A remote** is another copy you sync with (`push` / `fetch` / `pull`).

Notice what's underneath all five: **Git mostly doesn't destroy things - it moves labels and takes
snapshots.** That's why almost every "oh no" ahead turns out to be recoverable, and why the commands stop
feeling like magic spells.

Next: **[Phase 2 - the everyday commands](02-everyday-commands.md)** maps each command you already type
back to these five ideas.


---

# The Everyday Commands - What Each One Really Does

In Phase 1 you learned the five ideas: commits are snapshots, branches are sticky-note labels, HEAD is
"you are here," your work lives in three places, and the remote is another copy. Now we put them to
work - the same commands you already type, but you'll know what each one is *actually doing* to those
five things.

**In a hurry?** The cheat-card is right below. **Want it to stick?** Read the section under each
command - that's where the "what just happened" lives.

## The cheat-card

| Command | What it really does |
|---|---|
| `git status` | What's changed, what's staged, what branch you're on. Your dashboard - run it constantly. |
| `git add` | Move a file's current state into the staging area (the box for your next commit). |
| `git commit` | Save a snapshot of everything staged, with a message. A save point you can return to. |
| `git log` | The history of snapshots - who saved what, when, and why. |
| `git diff` | The exact lines that changed (working vs staged vs last commit). |
| `git branch` | List / create / delete the sticky-note labels that point at commits. |
| `git switch` / `checkout` | Move HEAD ("you are here") to another branch or commit. `switch` is the modern, safer name. |
| `git merge` | Combine another branch's commits into your current branch. |
| `git fetch` | Download the remote's new commits but DON'T touch your files. Only look. |
| `git pull` | `fetch` + `merge`: download remote commits and apply them now. |
| `git push` | Upload your commits to the remote so others get them. |
| `git stash` | Shelve uncommitted changes for a clean tree; `pop` them back later. |

---

## `git status` - your dashboard

Looks at all three places from Phase 1 - working directory, staging box, last commit - and reports how
they differ, plus your current branch. Read-only; changes nothing. Run it constantly: before you add,
before you commit, any time you're unsure what state you're in.

```console
$ git status
On branch main
Changes to be committed:
  modified:   checkout.js
Changes not staged for commit:
  modified:   cart.js
Untracked files:
  notes.txt
```
*What just happened:* All three places at once. `checkout.js` is in the staging box (goes in your next
commit). `cart.js` has edits *not* in the box yet. `notes.txt` is "untracked" - Git has never seen it and
ignores it until you `add` it.

**The gotcha.** There isn't one, and that's the point - `status` never changes anything, so run it as
often as you like. When in doubt, `git status`.

## `git add` - put changes in the box

Copies the current state of a file into the staging box - it saves nothing to history, it's packing the
box, not taping it shut. Reach for it right before committing, to choose exactly what goes in: edit ten
files, commit only three, keep unrelated changes out of the snapshot.

```console
$ git add checkout.js
$ git status
On branch main
Changes to be committed:
  modified:   checkout.js
```
*What just happened:* `checkout.js` moved into the box. Nothing is in history yet - you've only decided
what the next snapshot will include.

**The gotcha.** `add` captures the file *as it is right now*. Edit it again afterward and those new edits
aren't in the box - the file shows as both staged and not staged (you met this in Phase 1); `git add` it
again to fix that. Many people reflexively run `git add .`, which boxes up *everything* - convenient, and
also how stray files and stray debug code sneak into commits. Know what you're adding.

## `git commit` - tape the box shut

Takes everything in the staging box and turns it into a permanent snapshot, whose parent is wherever
HEAD is, then slides your branch's sticky note forward onto it (all five Phase 1 ideas in one command).
Reach for it at every meaningful checkpoint - commits are free, make them often.

```console
$ git commit -m "Fix tax rounding at checkout"
[main a1b2c3d] Fix tax rounding at checkout
 1 file changed, 3 insertions(+), 1 deletion(-)
```
*What just happened:* Git sealed the box into commit `a1b2c3d`, recorded its parent, and slid the `main`
label onto it. The summary line is Git being friendly about what changed versus the parent.

**The gotcha.** `commit` only saves what's in the box - edited a file but forgot to `add` it? It won't be
in the commit, and Git won't warn you (run `git status` first; see why it's a habit?). Also: `git commit`
with no `-m` drops you into a text editor for the message. If that's Vim and you're trapped, type `:q!`
and press Enter to escape without committing.

## `git log` - the history of snapshots

Walks backward from HEAD, following each commit's parent pointer, and lists the snapshots it finds -
how you read the chain from Phase 1. Reach for it to see what happened and when, or to find a commit's
hash to point another command at.

```console
$ git log --oneline
a1b2c3d (HEAD -> main) Fix tax rounding at checkout
9f2a1c7 Add login button
3e4f5a6 Initial commit
```
*What just happened:* Each line is a commit, newest first. `--oneline` squeezes each to its short hash
and message. `(HEAD -> main)` on the top one marks the "you are here" arrow and the `main` label, both on
the newest commit.

**The gotcha.** Plain `git log` can drop you into a full-screen pager - press `q` to quit. Two
lifesavers: `git log --oneline` for the compact view, and `git log --oneline --graph --all` to *see*
your branches drawn as a tree, the best way to make sense of a tangled history.

## `git diff` - show me the actual lines

Compares two of your three places and shows the exact line-by-line changes between them - by default,
working files against the staging box. Reach for it right before you `add` or `commit`, to review what
you're about to include.

```console
$ git diff
diff --git a/cart.js b/cart.js
@@ -14,7 +14,7 @@
-  const total = price
+  const total = price * quantity
```
*What just happened:* The change in `cart.js` that is *not yet staged*: one line removed, one added -
the diff between your working file and the box.

**The gotcha.** This confuses everyone: once you `git add` a file, plain `git diff` shows *nothing* for
it, because `diff` compares working-vs-box and `add` made them identical. Your change isn't gone, it's in
the box - use `git diff --staged` to see what's there. Remember it as: plain `diff` = "not added yet,"
`diff --staged` = "added, about to commit."

## `git branch` - manage the sticky notes

Lists, creates, or deletes branch labels. Creating one drops a new sticky note on your current commit -
it does not move you onto it. Reach for it to start a new line of work or see what branches exist.

```console
$ git branch
* main
  feature-login
$ git branch feature-cart
```
*What just happened:* The first command listed your branches (`*` marks the one you're on); the second
created `feature-cart` on your current commit. You're *still on `main`* - creating a branch doesn't move
you.

**The gotcha.** `git branch feature-cart` creates the branch but does *not* switch to it - people expect
to "be on" the new branch and start committing, then find their commits landed on `main`. To create *and*
switch in one step, use `git switch -c feature-cart` (next command).

## `git switch` (and `git checkout`) - move "you are here"

Moves HEAD onto a different branch and updates your working files to match that branch's commit.
`switch` is the modern, purpose-built command; `checkout` is the older one that also does several
unrelated jobs. Reach for it every time you change what you're working on.

```console
$ git switch -c feature-cart
Switched to a new branch 'feature-cart'
```
*What just happened:* `-c` created the `feature-cart` label and moved HEAD onto it in one step. Your next
commit slides *its* sticky note forward, leaving `main` where it was.

**The gotcha.** `checkout` is overloaded - the same command switches branches, restores files, and
detaches HEAD depending on how you call it. That's why `git checkout .` has erased people's work (it can
mean "throw away my changes"). Modern Git split those jobs: `git switch` for branches, `git restore` for
files - prefer them. Also: if uncommitted changes would be overwritten by a switch, Git blocks you -
`commit` or `stash` first (see `stash` below).

## `git merge` - combine two branches

Joins another branch's commits into your current one. If your branch hasn't moved since the other split
off, Git slides your label forward ("fast-forward"); otherwise it creates a *merge commit* with two
parents, stitching the histories together. Reach for it to bring a finished feature branch into `main`.

```console
$ git switch main
$ git merge feature-cart
Updating a1b2c3d..f6g7h8i
Fast-forward
 cart.js | 24 ++++++++++++++++++
```
*What just happened:* Because `main` hadn't moved, this was a fast-forward - Git slid `main`'s label up
to `feature-cart`'s commit. No merge commit needed; the histories were already in a straight line.

**The gotcha.** When both branches changed the *same lines*, Git can't decide which to keep and stops
with a **merge conflict**. That's not you doing something wrong - it's Git refusing to guess. Phase 3
walks through resolving one calmly.

## `git fetch` - download, but look before you leap

Downloads new commits from the remote into your local bookmark of *its* branches (like `origin/main`)
without touching your own branches or files - "show me what's out there, don't change anything." Reach
for it when you want to see what teammates pushed *before* deciding to integrate it.

```console
$ git fetch
remote: Enumerating objects: 5, done.
From github.com:acme/shop
   a1b2c3d..b2c3d4e  main -> origin/main
$ git log --oneline main..origin/main
b2c3d4e Add promo banner
```
*What just happened:* Git downloaded the new commit and updated `origin/main` - your bookmark of the
remote's `main`. Your own `main` hasn't moved. The second command lists what origin has that you don't.

**The gotcha.** `fetch` is safe because it doesn't touch your files - but that's also why people forget
they've fetched and wonder why nothing looks different. Fetching updates your *knowledge* of the remote;
merging (or pulling) is what brings those commits into your branch.

## `git pull` - fetch and merge in one step

Two things back-to-back: `git fetch`, then `git merge` - a convenience combo. Reach for it when you want
your branch caught up and expect the merge to be clean.

```console
$ git pull
Updating a1b2c3d..b2c3d4e
Fast-forward
 banner.js | 18 ++++++++++++++
```
*What just happened:* Git fetched the "Add promo banner" commit and immediately fast-forwarded your
`main` onto it. One command, fully synced.

**The gotcha.** Because `pull` *merges automatically*, it can drop you straight into a merge conflict (or
a surprise merge commit) when you and the remote have both moved. Many people prefer `git fetch`, look,
then merge deliberately - staying in control of *when* the merge happens. If a `pull` leaves you
mid-conflict, Phase 3 has you.

## `git push` - send your commits up

Uploads commits you have but the remote doesn't, and moves the remote's branch label forward to match
yours - the mirror image of `fetch`. Reach for it to share your work: back it up, open a pull request,
let teammates see it.

```console
$ git push
Enumerating objects: 5, done.
To github.com:acme/shop.git
   b2c3d4e..c3d4e5f  main -> main
$ git push -u origin feature-cart
```
*What just happened:* The first push sent your commits and advanced origin's `main`. The second is what
you run the *first* time you push a new branch: `-u origin feature-cart` creates the branch on the remote
and links your local branch to it, so future `push`/`pull` need no arguments.

**The gotcha.** If the remote has commits you don't, Git rejects the push (`fetch first`) rather than
bury history you haven't seen - `pull`, then push again. And a warning for later: `git push --force`
overrides that safety and can *erase your teammates' commits*. Never force-push a shared branch. (The
safe way to rewrite history is its own topic - guide #2.)

## `git stash` - shelve it for a minute

Takes your uncommitted changes (staged and unstaged), saves them on a stack, and reverts your working
directory to a clean state matching the last commit. Later, you pop them back. Reach for it when you're
mid-change and need a clean tree *right now* - an urgent fix, or a pull without committing half-finished
work.

```console
$ git stash
Saved working directory and index state WIP on feature-cart: a1b2c3d
$ git switch main          # clean tree, so the switch is allowed
$ # ...do the urgent thing, then come back...
$ git switch feature-cart
$ git stash pop
```
*What just happened:* `stash` swept your in-progress edits onto a shelf, handing you a clean working
directory so Git allowed the switch. `stash pop` later put those edits right back.

**The gotcha.** The stash is a *stack*, and it's easy to forget what's on it - stash twice, change
context for a week, and you've got mystery shelves. Use `git stash list` to see them, and prefer `git
stash pop` (applies *and* removes) so they don't pile up. For anything you want to keep more than a few
hours, a real commit on a branch is safer than a stash.

---

## You now speak Git

Every command above was moving the same five things from Phase 1: taking snapshots (`commit`), sliding
labels (`branch`, `merge`, `push`), moving "you are here" (`switch`), shuffling work between the three
places (`add`, `diff`, `stash`), or syncing copies (`fetch`, `pull`, `push`). None of it was magic - tools
acting on a model you now understand.

Which means you're ready for the part that used to be terrifying: **[Phase 3 - When It Breaks](03-when-it-breaks.md)**,
where things go wrong and you fix them without breaking a sweat.

Watch it animated: [merging two branches](/explainers/Merging.dc.html)

## Try it yourself

Run commands and watch the history graph build - try `commit -m "first"`, `branch dev`, `checkout dev`, `commit -m "work"`, `checkout main`, then `merge dev`:

```playground-git
```


---

# When It Breaks - Common "Oh No" Moments, Calmly Fixed

This is the phase you came for. Something went wrong, your heart rate is up, and you want it fixed
without making it worse.

Two promises. First: the cheat-card below gives you the fix immediately, no reading required. Second:
under each one, I'll show you *why* the fix works - and you'll notice it's always the same five ideas
from Phase 1. Branches are labels you can move. Commits are snapshots that don't vanish. Once you see
that, "disaster" downgrades to "minor inconvenience."

One rule before we start: **when something looks broken, stop and run `git status`.** It almost always
tells you exactly where you are and what your options are. Panic-typing commands is what turns a small
mess into a big one.

## The emergency cheat-card

> **In a panic? Find your situation, breathe, then read the section below it.**

| "Oh no…" | The calm fix |
|---|---|
| I committed to the wrong branch | Make the right branch here, move the wrong one back (§1) |
| Undo my last commit but keep my work | `git reset --soft HEAD~1` (§2) |
| Merge conflict, markers everywhere | Edit the file, delete the `<<<<`/`====`/`>>>>` markers, `add`, commit (§3) |
| Typo in my last commit message | `git commit --amend` - **only if you haven't pushed** (§4) |
| I staged the wrong file | `git restore --staged <file>` (§5) |

---

## 1. "I committed to the wrong branch"

**The situation.** You're heads-down, you commit - and then you see it. That commit was supposed to land
on `feature-cart`. You were on `main`.

**What's actually happening.** Remember from Phase 1: the commit went onto whatever HEAD pointed at, and
`main` slid forward onto it. Nothing is broken - the commit exists, it's just attached to the wrong
label. You need to (a) get the commit onto the right branch, and (b) move `main` back.

**The calm fix** (for a commit you have *not* pushed yet):
```console
$ git switch -c feature-cart      # make the right branch HERE, taking the commit with you
Switched to a new branch 'feature-cart'

$ git switch main                 # go back to main...
$ git reset --hard HEAD~1         # ...and move main's label back one commit
```
*What just happened:* `git switch -c feature-cart` created that label on the commit you made and moved
you onto it - your work is now safely on `feature-cart`. Then you switched back to `main` and moved its
label back by one (`HEAD~1` = "one commit before HEAD"). You never *moved* the commit - you labeled it
correctly, then slid the wrong label back.

**⚠ The `--hard` warning.** `git reset --hard` throws away any uncommitted changes in your working
directory. Here it's safe *because* your work is already saved on `feature-cart`. But never run `--hard`
when you have unsaved edits you care about - it deletes them with no undo. Unsure? Run `git status`
first to confirm there's nothing uncommitted to lose.

**How to avoid it next time.** Glance at the branch line in `git status` (or your shell prompt) before
committing.

## 2. "Undo my last commit - but keep my work"

**The situation.** You committed too early, or the commit was a mistake - but you do *not* want to lose
the code. You want to rewind the commit and keep the changes.

**What's actually happening.** "Undoing a commit" means moving your branch label back to the parent
commit. The only real question is what happens to the *changes* from the commit you're undoing - the
single difference between the three forms of `reset`:

```mermaid
flowchart LR
  C1 --> C2 --> C3
  main(main) -.->|"reset moved it back"| C2
```
The label moves from C3 back to C2. The three flavors decide what happens to C3's *contents*:

- **`git reset --soft HEAD~1`** - keep C3's changes **staged**, in the box, ready to re-commit. (As if
  you never hit commit.)
- **`git reset --mixed HEAD~1`** - the default; keep C3's changes in your files but **unstaged**.
- **`git reset --hard HEAD~1`** - throw C3's changes away. Gone.

**The calm fix** (keep the work - the common case):
```console
$ git reset --soft HEAD~1
$ git status
On branch main
Changes to be committed:
  modified:   checkout.js
```
*What just happened:* `main` moved back one commit, and the undone commit's changes are sitting staged,
exactly as they were. Edit, re-stage, and commit again whenever you're ready.

**⚠ The `--hard` warning.** `--hard` is the one that ruins afternoons - it deletes the changes, not only
the commit. Reach for `--soft` or `--mixed` unless you're *certain* you want the work gone. This whole
technique is for commits you **haven't pushed**; rewinding history others already have is a different,
more careful game - guide #2.

> **War story.** Early in my career I found `git reset --hard HEAD~1` on the internet to fix a typo, and
> ran it without knowing `--hard` also meant "delete my uncommitted files." It erased a morning of work
> nobody had warned me about. That's why this guide leads with the *meaning* of every command, not the
> command.

## 3. "I have a merge conflict and I'm terrified"

**The situation.** You merged (or pulled), and Git stopped cold with `CONFLICT (content): Merge conflict
in cart.js`. Strange `<<<<<<<` markers are in your file and you're sure you broke something.

**What's actually happening.** You broke nothing. A conflict means two commits changed the *same lines*,
and Git - true to form - refuses to guess which wins (Phase 1: Git won't silently clobber). It paused the
merge and is asking *you* to decide. That's all a conflict is: an unfinished merge, waiting on a human.

**The calm fix.** Open the conflicted file. You'll see your two options, fenced by markers:
```text
<<<<<<< HEAD
const total = price * quantity          (your version - what's on your branch)
=======
const total = price * qty               (their version - the branch you're merging)
>>>>>>> feature-cart
```
1. Edit the file so it reads exactly how you want the final result to look.
2. **Delete all three marker lines** (`<<<<<<<`, `=======`, `>>>>>>>`).
3. Stage the resolved file and finish the merge:
```console
$ git add cart.js
$ git commit            # completes the merge (Git pre-fills a message for you)
```
*What just happened:* You told Git the final text for the conflicting lines, removed the markers, staged
the result, and committed - finishing the merge it had paused.

**The escape hatch.** Not ready to deal with it? `git merge --abort` puts everything back exactly as it
was before you started - safe to run any time you're mid-conflict and want out.

**How to avoid it next time.** Conflicts come from divergence, so pull/integrate often and keep changes
small. You can't prevent them entirely - and now you don't need to.

## 4. "There's a typo in my last commit message"

**The situation.** You committed "Fix taht bug," and now it's staring back at you.

**What's actually happening.** A commit's message is part of the commit - you can't edit it in place, but
you can *replace* the last commit with an identical one that has a better message.

**The calm fix:**
```console
$ git commit --amend -m "Fix that bug"
[main 7h8i9j0] Fix that bug
 1 file changed, 2 insertions(+)
```
*What just happened:* `--amend` replaced your last commit with a new one - same changes, corrected
message. The hash changed (`7h8i9j0`): it's technically a brand-new commit that took the old one's place.

**⚠ The big warning.** Because `--amend` creates a *new* commit with a *new* hash, it rewrites history -
harmless for a commit that lives only on your machine. But if you already **pushed** that commit,
amending makes your history disagree with the remote's; your next push gets rejected, and force-pushing
stomps on anyone who already pulled. Rule of thumb: **amend freely before you push; think twice after.**
Fixing already-pushed commits safely is guide #2.

## 5. "I staged the wrong file" / "I changed my mind"

**The situation.** You ran `git add` on something you didn't mean to - a debug file, a half-finished
change - and want it *out* of the next commit, without losing your edits.

**What's actually happening.** The file is in the staging box (Phase 1). You want to take it out of the
box while leaving your actual edits untouched in your working directory.

**The calm fix:**
```console
$ git restore --staged debug.log
$ git status
On branch main
Untracked files:
  debug.log
```
*What just happened:* `git restore --staged` removed `debug.log` from the box. Your file and its
contents are untouched - only its "staged" status changed. (On older Git this is written as `git reset
HEAD debug.log`; same effect.)

**⚠ The dangerous look-alike.** `git restore debug.log` - *without* `--staged` - does something very
different: it throws away your working-directory edits and reverts the file to its last committed state.
Memorize the pair:
- `git restore --staged <file>` → unstage, **keep** your edits. (Safe.)
- `git restore <file>` → discard your edits, revert the file. (Destructive - no undo.)

**How to avoid it next time.** Run `git diff --staged` before committing to see exactly what's in the
box. Something surprising there? `restore --staged` it back out.

---

## You're not afraid of Git anymore

Notice what every fix in this phase had in common: none of them were magic incantations. Each one was
you - moving a label, or reading a snapshot - with full knowledge of what Git was doing, because you
learned the model first.

That's the whole point of this guide. Git was never the haunted house it pretended to be. It's a small
set of no-nonsense tools: snapshots that don't vanish, labels you can move, three places your work can live,
and copies you keep in sync.

**Where to go next.** When you're ready for the advanced nightmares - recovering commits you thought were
gone (the reflog), safely undoing history you've *already pushed*, and rescuing a rebase that went
sideways - that's the next guide. You now have the foundation that makes all of it make sense.
