Recover Git Files Safely with git restore
You will use git restore to put files back as they were in the index or a chosen commit, unstage changes without touching the working tree, and recover a deleted tracked file. The command changes files or the index, so inspect the diff first and keep a copy of anything you may need. These examples use Git 2.43.0 from the installed git-man package, whose manual labels git restore experimental. Allow about 10 minutes for the examples, plus time to review your own diff.
The route
Jump straight to the step you need, or tick off Done means at the end.
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 beginning with M has a working-tree change. A line beginning with M has a staged change. The first 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 and copy the file or make a temporary branch before restoring it.
2. Discard working-tree changes in one file
With no location option, git restore updates the working tree from the index. It 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 when README.md now matches the index. The -- marks the end of options, which prevents a filename beginning with a hyphen being read as a flag.
Warning
This discards the file's unstaged content. Git does not provide an undo command for that working-tree overwrite. Recover it from an editor history, backup, stash, or another copy. Do not use this form 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, specify both locations and make the source explicit:
git restore --source=HEAD --staged --worktree -- README.md
git status --short -- README.md
A clean result for that path is no output. This is destructive for both staged and unstaged edits, so review both diffs before running it.
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 remains 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. 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 return to the index with git restore -- Makefile.
You can restore several paths, or a quoted glob interpreted by Git rather than by 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 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 final command should print notes.txt recovered. The rm here is only a demonstration of the state being repaired. Do not run it on a real file unless the deletion is intentional.
If the file was also staged for deletion, the index no longer contains the version to use as the default source. Recover both locations from HEAD:
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, Git's meanings of ours and theirs can appear reversed because the commits being replayed change roles. If you need the conflict markers back instead, use git restore --merge -- path/to/conflicted-file, then resolve them manually.
These conflict options work when restoring from the index. They cannot be combined with --source. They also 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 contains both 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. It can be run without a path to offer all modified paths, but naming the file keeps the review smaller and easier to audit. A rejected hunk remains in place.
8. Avoid the common traps
git restore filenormally changes the working tree from the index, not directly fromHEAD. Use--source=HEADwhen that is the result you mean.--stagedchanges only the index unless you also add--worktree. It is not a synonym for "discard all edits".- The default is no-overlay mode 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. - Submodule working trees are not updated unless
--recurse-submodulesis requested. When it is requested, local submodule changes can be overwritten and itsHEADbecomes detached. - Neither
git restorenor these examples require elevated privileges. Running them withsudocan create root-owned files and hides the repository identity you intended to use.
Done means
git status --shortshows only changes you recognise.git diffandgit diff --cachedcontain the content you intend to keep.- Every restored path was checked before being staged or committed.
- You kept a backup or recovery route for any discarded work.