Undo Local Changes Safely with git restore
Use git restore when you have edited, staged or deleted the wrong file and want the working tree or the index put back the way it was. It can unstage a change without touching your edits, pull a file back from an old commit, or bring back something you just deleted. These examples use Git 2.43.0 from the installed git-man package, whose manual still labels git restore experimental. Give it about 10 minutes, plus time to read your own diff before you run anything.
The route
Jump straight to the step you need, or tick off Done means at the end.
- You need: a Git repository containing the file in question, and a moment to check
git diffbefore overwriting anything. - It will not touch: files outside the paths you name; there is no
--allshortcut here.
1. Check what Git thinks has changed
Start in the repository that contains the file. This step is read-only and separates working-tree changes from staged changes.
cd /path/to/project
git status --short
git diff -- README.md
git diff --cached -- README.md
- A line starting with a space then M is a working-tree change.
- A line starting with M then a space is a staged change.
- The plain diff compares your file with the index.
- The cached diff compares the index with
HEAD.
If you cannot explain a change shown by these commands, stop. Copy the file or make a temporary branch before restoring anything.
2. Discard working-tree changes in one file
With no location option, git restore updates the working tree from the index and leaves a staged version alone. This is the usual command for throwing away edits made after the last git add.
git restore -- README.md
git diff --exit-code -- README.md
The second command should produce no output and return success once README.md matches the index. The -- marks the end of options, so a filename beginning with a hyphen is not read as a flag.
Warning
This discards the file's unstaged content, and Git has no undo command for that overwrite. Recover it from an editor history, backup, stash or another copy. Do not run this until git diff shows nothing you still need.
3. Unstage a file without losing its edits
Add --staged when the index is the thing to restore. Without an explicit source, Git uses HEAD for a staged restore.
git restore --staged -- README.md
git status --short
git diff -- README.md
Afterwards the staged status should be gone, while the file's edits appear in the ordinary working-tree diff. This is a safe way to correct an over-broad git add: it changes what the next commit would contain, not the file on disk.
To make both the index and working tree match HEAD, name both locations and make the source explicit:
git restore --source=HEAD --staged --worktree -- README.md
git status --short -- README.md
Warning
That command is destructive for both staged and unstaged edits on that path, so review both diffs first. A clean result afterwards is no output at all.
4. Restore content from another commit
Use --source with a commit, branch or tag. The command below replaces only the working-tree copy with the version from two commits ago; the index stays as it was.
git restore --source=HEAD~2 -- Makefile
git diff -- Makefile
git diff --cached -- Makefile
The first diff shows the difference between the recovered file and the index, so check it before staging anything. If it is the version you want, run git add -- Makefile and review git diff --cached. If it is not, restore from another revision or go back to the index with git restore -- Makefile.
You can restore several paths at once, or a quoted glob for Git itself to interpret rather than the shell:
git restore --source=release-2025 -- '*.c'
Keep the quotes. They let Git match tracked C files even when one is not currently present in the working tree. A broad path such as . affects every matching path below the current directory, so use it only after checking the complete diff.
5. Recover a deleted tracked file
If a tracked file was removed from the working tree but its last version is still in the index, restore it without naming a source:
rm notes.txt
git status --short
git restore -- notes.txt
test -f notes.txt && echo "notes.txt recovered"
The status line should change from a deletion to clean for that path, and the last command should print notes.txt recovered. The rm here only demonstrates the state being repaired: do not run it on a real file unless you actually mean to delete it.
If the file was also staged for deletion, the index no longer holds the version to use as a default source. Recover both locations from HEAD instead:
git restore --source=HEAD --staged --worktree -- notes.txt
6. Handle a merge conflict deliberately
During an unresolved merge, the index can hold separate stages. To take one side into the working tree, use --ours or --theirs without --source:
git restore --ours -- path/to/conflicted-file
# or
git restore --theirs -- path/to/conflicted-file
git add -- path/to/conflicted-file
Inspect the result before staging it. During a rebase or git pull --rebase, ours and theirs can appear reversed because the commits being replayed swap roles. If you want the conflict markers back instead, run git restore --merge -- path/to/conflicted-file and resolve them by hand.
These conflict options only work when restoring from the index, cannot be combined with --source, and need an actual unmerged path. On an ordinary file they do not mean "choose the current branch".
7. Use patch mode for selected hunks
If a file mixes useful and unwanted edits, let Git ask about individual hunks:
git restore --patch -- README.md
Review each displayed hunk and answer with the prompt's choice. Patch mode restores selected working-tree changes from the index by default. Run it without a path to offer every modified file, but naming the file keeps the review smaller and easier to audit. A rejected hunk stays in place.
Common traps
- Assuming a source-free restore reads from HEAD.
git restore filenormally changes the working tree from the index, not directly fromHEAD. Use--source=HEADwhen that is what you mean. - Treating --staged as "discard everything". It changes only the index unless you also add
--worktree. - Missing the no-overlay default. When a source tree is supplied, a tracked file absent from that source can be removed. Use
--overlaywhen you need existing files left in place. - Forgetting submodules. Their working trees are not updated unless
--recurse-submodulesis requested, and requesting it can overwrite local submodule changes and detach itsHEAD. - Reaching for sudo. Neither
git restorenor these examples need elevated privileges. Running them withsudocan create root-owned files and hides the repository identity you meant to use.
Done means
- git status --short shows only changes you recognise.
- git diff and git diff --cached contain exactly the content you intend to keep.
- Every restored path was checked before being staged or committed.
- Any discarded work had a backup or recovery route first.