TechEarl

How to Discard Local Changes in Git

Discard local changes in Git with restore, checkout, and clean. How to throw away edits to tracked files, delete untracked files, and reset to the last commit.

Ishan Karunaratne⏱️ 7 min readUpdated
Share thisCopied
How to discard local changes in Git with git restore, git checkout, and git clean to reset your working tree to the last commit.

To throw away edits to tracked files and go back to the last commit, run git restore <file> for one file or git restore . for all of them. On Git before 2.23, use git checkout -- <file> instead. To delete files Git is not yet tracking, you need git clean -fd. None of this is recoverable once it runs, so be sure before you press enter.

That is the whole answer. The rest of this page explains which command matches which situation, because "discard local changes" turns out to mean three slightly different things depending on whether a file is tracked, staged, or brand new.

What "local changes" actually means

When you edit files in a repository, Git sorts them into states. Knowing the state tells you which command to reach for. If the difference between these is fuzzy, the staging area walkthrough is worth ten minutes first.

StateWhat it meansHow to discard it
Modified, tracked, unstagedYou edited a file Git already knows about, no git add yetgit restore <file>
Modified, tracked, stagedSame, but you ran git add on itgit restore --staged <file> then git restore <file>
UntrackedA new file Git has never seen (no git add ever)git clean -f
EverythingThrow away the whole working treegit reset --hard plus git clean -fd

Run git status first, every time. It tells you exactly which bucket each file is in, and it spells out the discard command for that bucket right in its output. Reading status before discarding is the single habit that prevents most "I lost my work" moments.

Discard changes to a tracked file

This is the common case: you changed a file, the change is wrong, and you want the file back the way it was at the last commit.

bash
# discard changes to one file
git restore path/to/file.js

# discard changes to everything in the working tree
git restore .
Discarding tracked and untracked changes with git restore and git clean
git restore drops tracked edits; git clean -fd removes untracked files.

git restore arrived in Git 2.23 (August 2019) to split the overloaded git checkout into two clearer commands: git restore for files, git switch for branches. It is the modern, recommended way to do this.

On an older Git, the equivalent is:

bash
# pre-2.23 syntax, still works on every version
git checkout -- path/to/file.js
git checkout -- .

The -- matters. It tells Git "everything after this is a file path, not a branch or flag," which avoids ambiguity if a file happens to share a name with a branch. Both forms do the same thing: overwrite your working copy with the version from HEAD, the last commit on your current branch.

Discard a change you already staged

If you ran git add on the file, it is staged, and a plain git restore will not touch it. Unstage it first, then discard:

bash
# step 1: move it out of the staging area (the file keeps your edits)
git restore --staged path/to/file.js

# step 2: now discard the edits in the working tree
git restore path/to/file.js

The two-step is deliberate. git restore --staged only resets the staged copy back to HEAD; your edits are still sitting in the working file. The second git restore is what actually throws those edits away. If you only want to unstage and keep editing, stop after step one. A common reason to be here at all is that a pull or merge refused to run and complained that your local changes would be overwritten by merge; discarding the staged and working edits is one way to clear that block. For undoing a commit rather than staged edits, see undoing the last commit and the reset vs revert comparison.

Delete untracked files with git clean

Here is the trap that catches beginners: git restore and git checkout only operate on files Git is already tracking. A brand-new file you have never committed is untracked, and Git will leave it completely alone. To remove untracked files you need git clean.

Always do a dry run first. -n shows what would be deleted without deleting anything:

bash
# DRY RUN: list what clean would remove, delete nothing
git clean -n

Once you are happy with that list, run it for real:

bash
# delete untracked files
git clean -f

# delete untracked files AND untracked directories
git clean -fd

# also remove files your .gitignore is ignoring (build output, etc.)
git clean -fdx

The flags:

  • -f is "force." Git refuses to delete files without it, on purpose, because this is destructive.
  • -d extends the delete to untracked directories, not just loose files.
  • -x also removes files matched by your .gitignore rules, like node_modules or a build/ folder. Use this one carefully; it wipes things you usually want to keep, like local env files.

git clean -fdx is the nuclear option for a truly pristine checkout. I reach for it when a build is poisoned by stale artifacts and I want the directory to look exactly like a fresh git clone.

Throw everything away and reset to the last commit

To return the entire working tree to the last commit, both tracked edits and untracked junk, you need two commands. reset --hard handles tracked files; clean -fd handles the untracked ones.

bash
# discard all changes to tracked files
git reset --hard

# then remove every untracked file and directory
git clean -fd

git reset --hard rewinds tracked files to HEAD and blows away both staged and unstaged edits in one move. It does not touch untracked files, which is why the clean is still needed for a complete wipe. If you want to reset to a specific commit rather than the current one, name it: git reset --hard <commit-hash>.

A safer middle ground, if you might want the work back later, is to stash instead of discard:

bash
# tuck everything away instead of deleting it
git stash --include-untracked

That puts your changes on a stack you can restore with git stash pop. When you are not fully sure you want the work gone, stash it; the git stash guide covers the workflow. Stashing costs nothing and buys you an undo.

This is irreversible. Really.

The commands on this page do not move files to a trash can. They overwrite or delete them at the filesystem level. Git's safety net, git reflog, only tracks committed history. Edits you never committed leave no trace once git restore, git reset --hard, or git clean runs.

In plain terms:

  • Committed work can almost always be recovered, even after a bad reset --hard, using the reflog. The commit is still in the object database.
  • Uncommitted work (anything you discard here) is gone. No reflog entry, no dangling commit, nothing to recover.

So the discipline is simple: run git status to see what you are about to lose, run git clean -n before any real git clean, and when in doubt, git stash instead of discard. Committing early and often also helps, because a commit is a recoverable checkpoint. If your commit cadence needs work, the Git for Beginners hub and the commit message conventions are good next reads.

FAQ

Sources

Authoritative references this article was fact-checked against.

Tagsdiscard local changes gitGitVersion Controlgit restoregit cleanDevOps

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Resolve Merge Conflicts in Git

A merge conflict means Git found two changes to the same lines and needs you to pick. Here is how to read the conflict markers, fix them by hand or with a mergetool, and commit.

How to Disable Root Login on Linux

Disable direct root login over SSH and on the console, lock the root password, and move everyone to a normal account plus sudo, without locking yourself out.