Git Discard Changes: Restore, Reset, and Clean Local Edits

Tested on RHEL 10.2 (Coughlan)
Package git 2.52.0
Applies to Any host with Git installed
Privilege Normal user
Scope Discard current uncommitted edits in the working tree and staging area with git restore, git reset --hard, and git clean. Brief notes on stash and committed history. Scenario sections match git status output — not a full revert, reset, or history-restore deep dive.
Related guides Git restore examples
Git reset examples
Git clean
Git unstage files
Git stash explained

Discarding changes means throwing away local edits you no longer want. Git stores those edits in different places — modified files, the staging area, untracked paths, or commit history — and each place has its own command.

Start with git status -sb. The sections below are independent scenarios keyed to what that output shows. Pick the row that matches your status line — not a lab sequence to run top to bottom.


Choose the Right Discard Command

Git has no single discard subcommand. Match your status line to the smallest command that does what you need:

git status -sb What you want Command
M file Discard unstaged change git restore file
M file Unstage, keep content on disk git restore --staged file
M file Discard the staged change and restore the file to HEAD git restore --staged --worktree file
MM file Discard both staged and unstaged content in this file git restore --staged --worktree file
?? file Remove untracked files preview git clean -n, then git clean -f
?? dir/ Remove untracked directories too preview git clean -nd, then git clean -fd
Many tracked changes Reset all tracked files to HEAD git reset --hard HEAD
Unsure about deleting Save first git stash push

Committed history is a different problem. This article focuses on uncommitted work; see Git revert examples or Git reset examples when the change is already a commit.


Check What Git Sees First

When to use this: you want to discard something but are not sure whether it is staged, unstaged, or untracked.

Short status with branch and change columns:

bash
git status -sb
output
## main
 M README.md
M  app.txt
MM config.yml
?? scratch.txt

Read the two change columns from left to right:

  • First column — staging area: M means staged; space means not staged.
  • Second column — working tree: M means modified on disk.
  • ?? — untracked; git restore and git reset --hard do not remove these paths.

MM means you staged one version, then edited the file again afterward, so Git now has one changed version in the index and another changed version in the working tree. For one file, git restore --staged --worktree file resets both from HEAD in a single command — you do not need git reset --hard HEAD unless many tracked files must go.


Discard Unstaged Changes

Scenario: you edited a tracked file and have not run git add, or you already unstaged it. Status shows a space before M:

text
## main
 M app.txt

Plain git restore updates the working tree from the index, not directly from HEAD:

bash
git restore app.txt

When nothing is staged for that file, the index normally matches HEAD, so the unstaged edit disappears. If the file is also staged (M in the first column), plain git restore only realigns the working tree to the staged snapshot — use git restore --staged --worktree file when you want both gone.

After a successful restore, status should be clean for that path:

bash
git status -sb
output
## main

For every tracked file with an unstaged edit:

bash
git restore .

Staged entries and untracked ?? paths are left unchanged.


Unstage Without Deleting Changes

Scenario: you ran git add and want the file out of the index while keeping your edits on disk. Status shows M in the first column and a space in the second:

text
## main
M  app.txt

Move the file out of the staging area only:

bash
git restore --staged app.txt

The index no longer lists the change; disk content is unchanged:

bash
git status -sb
output
## main
 M app.txt

To unstage every staged file at once:

bash
git restore --staged .

When you also want to discard the staged change and restore the file to HEAD — not just move it back to the working tree — use git restore --staged --worktree file on that path instead.


Discard Staged and Unstaged Changes

Scenario: status shows MM on one file, or you need the entire repository back to the last commit.

One file (MM)

MM means Git holds one version in the index and a different version on disk. To discard both for a single path:

text
## main
MM config.yml

Restore index and working tree from HEAD in one step:

bash
git restore --staged --worktree config.yml

Because --staged is set and no --source is given, Git defaults the source to HEAD. Both copies match the last commit:

bash
git status -sb
output
## main

Prefer this over git reset --hard HEAD when only one file should change — --hard affects every tracked file in the repository.

Whole repository

When many tracked files are dirty and you intentionally want all of them to match HEAD:

bash
git reset --hard HEAD
output
HEAD is now at aca4fec add config

Confirm every tracked file matches HEAD:

bash
git status -sb
output
## main

git reset --hard HEAD does not remove untracked ?? paths — run git clean when those should go too.


Remove Untracked Files and Directories

Scenario: status lists ?? paths. Those files were never committed — restore and reset ignore them.

text
## main
?? build/
?? scratch.txt

Preview what git clean would delete:

bash
git clean -n
output
Would remove scratch.txt

For directories, preview with -d:

bash
git clean -nd

Delete untracked files and directories after you are satisfied with the preview:

bash
git clean -fd
output
Removing build/
Removing scratch.txt

Always run -n or -nd before -f or -fd on an unfamiliar repository. git clean -fd does not remove ignored files; -x includes them, so use -x only when that is intentional.


Save Changes With Stash Instead

Scenario: you need a clean tree but are not sure the edits can be permanently deleted.

git stash push saves tracked staged and unstaged changes. Add -u if you also need untracked files saved — by default, stash does not include ?? paths:

bash
git stash push -m "WIP before discard"

Use this before git reset --hard or git clean when you might want the work back. Untracked files left after stash still need git clean or git stash push -u if you want them preserved. See Git stash explained for apply, pop, and drop.


When Changes Are Already Committed

Scenario: git status is clean but the last commit (or an older one) should be undone.

This article stops at uncommitted edits. For shared branches, prefer git revert — it adds a new commit that undoes a prior one without rewriting history. Local-only cleanup may use git reset --soft, --mixed, or --hard against HEAD~1; see Git revert examples and Git reset examples for those workflows.

Never run git reset --hard on commits you already pushed unless your team expects a force push.


Troubleshooting

Symptom Likely cause Fix
git restore file ran but content still looks wrong File is staged (M or MM in first column) git restore --staged --worktree file for that path
Only part of an MM file was cleared Plain git restore realigns working tree to index, not HEAD git restore --staged --worktree file
git reset --hard ran but ?? files remain Untracked paths are outside reset scope git clean -fd after preview with git clean -n
Used --hard and lost edits on unrelated files --hard resets every tracked file Prefer git restore --staged --worktree file for single-file discard
Not sure you want permanent loss Destructive command on the wrong state git stash push first, then discard from a clean tree

References

Git documentation: git-restore
Git documentation: git-reset
Git documentation: git-clean
Git documentation: git-stash
Git documentation: git-status


Summary

Discarding changes in Git starts with git status -sb. The two change columns tell you whether edits live in the working tree, the staging area, or both — and ?? marks paths that only git clean removes.

Plain git restore file replaces the working-tree copy from the index; when nothing is staged, that matches HEAD. Use git restore --staged to unstage without deleting disk content, and git restore --staged --worktree file when one MM or fully staged file should match HEAD without touching anything else. Reserve git reset --hard HEAD for when every tracked file must go.

Preview untracked deletions with git clean -n before git clean -fd; ignored files need -x and are left alone by default. When you might need tracked work again, git stash push saves staged and unstaged edits — add -u when untracked paths should be included. Committed history belongs to git revert or git reset — pick the smallest command that matches your status line rather than reaching for --hard first.


Frequently Asked Questions

1. How do I discard changes in Git?

Run git status -sb first. Use git restore file for unstaged edits, git restore --staged --worktree file when both index and working tree differ on one file, git reset --hard HEAD to drop all tracked local edits, and git clean -fd for untracked paths after git clean -n.

2. How do I discard local changes in Git?

git restore file replaces the working-tree copy from the index for that file. git restore . does the same for every tracked file with unstaged edits. Neither removes untracked files — use git clean for those.

3. How do I discard all changes in Git?

git reset --hard HEAD removes staged and unstaged edits in every tracked file. Add git clean -fd when untracked files and directories must go too. Stash first if you might need the work later.

4. How do I discard unstaged changes in Git?

git restore file writes the index version back into the working tree. When nothing is staged for that file, the index matches HEAD, so the unstaged edit disappears. Use git restore . for every file showing a space then M in git status -sb.

5. How do I discard staged changes in Git?

git restore --staged file unstages without touching disk content. To throw away the staged snapshot too, use git restore --staged --worktree file on that file, or git reset --hard HEAD when every tracked file should match HEAD.

6. How do I delete untracked files in Git?

git clean -n previews removals. git clean -f deletes untracked files. git clean -fd also removes untracked directories. Ignored files need git clean -fx when you truly want them gone.

7. What is the safest way to undo changes in Git?

git stash push saves staged and unstaged tracked edits and gives you a clean tree without deleting history. For commits already on a shared branch, git revert adds a new undo commit instead of rewriting history.

8. What is the difference between git reset and git restore?

git restore is designed around restoring file or index content from a tree-ish such as HEAD. git reset adjusts HEAD, index, and working-tree state depending on mode and target; git reset --hard HEAD discards all tracked local edits when you intentionally reset the whole repository.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)