# Working tree, index and HEAD

> Three copies of your files — and every “undo” command is about which of them it changes.

Concept · How Git works · https://levelcode.org/concepts/git-three-areas

At any moment, each file in a Git repository exists in up to three places:

| Area | What it is | Changed by |
|---|---|---|
| **Working tree** | The files on disk that you edit | Your editor; `git restore`, `checkout`, `switch` |
| **Index** (staging area) | The snapshot that **the next commit will contain** | `git add`, `git rm`, `git restore --staged` |
| **HEAD** | The last commit on the current branch | `git commit`, `git reset`, `git switch` |

```text
working tree ──git add──▶ index ──git commit──▶ HEAD
```

`git status` is a comparison of the three. "Changes to be committed" means the index differs from HEAD. "Changes not staged" means the working tree differs from the index.

## Why the index exists

It lets you build a commit deliberately instead of committing everything at once:

```bash
git add -p                 # stage some hunks of a file, not all of it
git diff                   # working tree vs index (unstaged)
git diff --staged          # index vs HEAD (what you're about to commit)
```

## Undo commands, sorted by area

Most confusion about undoing comes from not knowing which areas a command touches:

| Command | HEAD | Index | Working tree |
|---|---|---|---|
| `git restore file` | — | — | ✔ reset to the index (discards edits) |
| `git restore --staged file` | — | ✔ reset to HEAD (unstages) | — |
| `git reset --soft HEAD~1` | ✔ moves back | — | — |
| `git reset HEAD~1` (mixed, default) | ✔ | ✔ | — |
| `git reset --hard HEAD~1` | ✔ | ✔ | ✔ **discards uncommitted work** |
| `git revert <commit>` | New commit that undoes it | ✔ | ✔ |

- **`--soft`** undoes the commit but keeps everything staged. Use it to redo a commit message or squash.
- **Mixed** undoes the commit and unstages, keeping your edits.
- **`--hard`** throws away work that was never committed, and Git can't bring that back. Committed work can still be recovered from the [reflog](https://levelcode.org/concepts/reflog).
- **`revert`** is the safe choice for commits already pushed. It adds history instead of rewriting it.

The [undoing changes lesson](https://levelcode.org/roadmaps/git/undoing-changes) walks through each one, and [refs and HEAD](https://levelcode.org/concepts/git-refs) explains what "moving HEAD" really means.
