GitDiscarding Local Changes

Discarding Local Changes

Sometimes you just want your working directory to look like it did at the last commit — edits gone, staged changes gone, scratch files swept away. Git treats each of those three things a little differently, so the trick is knowing which category your unwanted change falls into.

Three kinds of “local changes”
  • Unstaged edits — you modified a tracked file but did not git add it.

  • Staged edits — you ran git add but did not commit yet.

  • Untracked files — brand-new files Git has never seen. They will not be touched by restore or reset.

Discard unstaged edits

Throw away working-tree edits

Bash
# A single file
git restore src/util.js

# Multiple files
git restore src/util.js src/helpers.js

# Everything in the current directory and below
git restore .

# Legacy spelling (still works)
git checkout -- src/util.js
Warning
`git restore <file>` deletes your edits with no confirmation and no reflog entry for the working-tree state. There is no undo. If you might want the edits back, run `git stash` first.
Discard staged + unstaged edits

Reset a file all the way to HEAD

Bash
git restore --staged --worktree src/util.js
# Equivalent to a per-file hard reset

# All files
git restore --staged --worktree .

# Or the nuclear option (for tracked files only)
git reset --hard HEAD
Warning
`git reset --hard HEAD` discards every modification to every tracked file. It does NOT remove untracked files — those need `git clean` (next section).
Remove untracked files

Untracked files are invisible to restore and reset. To delete them you need git clean, which is intentionally a little awkward to type because it is so destructive.

git clean flags

Bash
git clean -n              # dry run — lists what WOULD be deleted (recommended first!)
git clean -nd             # dry run, include untracked directories
git clean -f              # actually delete untracked files
git clean -fd             # delete untracked files AND directories
git clean -fdx            # also delete files ignored by .gitignore
git clean -fdX            # ONLY delete files ignored by .gitignore (e.g. build output)
Warning
`git clean` permanently deletes files that Git has never seen — which means there is no Git history to recover them from. The reflog cannot help. **Always run with `-n` first** to see the list before adding `-f`.
The “start over” combo

Full reset of the working directory

Bash
# Dry run first
git status
git clean -nd

# Then the real thing
git reset --hard HEAD
git clean -fd

# Even more nuclear: also wipe ignored build output, node_modules, etc.
git clean -fdx
# (You will need to reinstall dependencies, regenerate caches, etc.)
Warning
This combo throws away every uncommitted change AND every untracked file in the repository. It is the right move when you really do want to match HEAD exactly — and the wrong move if you forgot you had work-in-progress in a scratch file. Always check `git status` first.
Dry-run everything

Command

Dry-run flag

What it shows

git clean

-n / --dry-run

Files that would be deleted

git restore -p

Interactive

You confirm each hunk before discarding

git reset (mixed)

--dry-run (since 2.34)

What would change in the index

git stash

(use stash itself)

Save your work first; verify by git stash list

Safer alternative: stash, do not discard

If you are even a little unsure, stash instead of discard. Stashed work is recoverable for 30+ days and costs you nothing.

Park your work in case you change your mind

Bash
git stash push -m "Maybe-bad experiment"
git status                      # clean — same effect as discarding

# Later, if you want it back
git stash list                  # find the right stash
git stash pop                   # put it back into the working tree
What can be recovered after a discard?
  • Discarded unstaged edits — gone. Not in the reflog, not in the object database.

  • Discarded staged edits — gone, with the same caveat.

  • Deleted untracked files (via git clean) — gone. Not in Git at all.

  • Hard-reset commits — recoverable via the reflog for ~90 days, since the commits themselves are objects in .git/objects.

Mental model
Git happily preserves your work as long as it has been **committed at least once**. Anything that has only lived in the working tree or the index is invisible to the reflog and cannot be brought back once `restore`, `reset --hard`, or `clean` removes it.
Tip
Make a habit of running `git status` and `git clean -nd` *before* the destructive command. The two seconds of looking will save you hours of recreation work the day you mis-type a path.