Git Unstage Files: git restore --staged, git reset HEAD, and git rm --cached

Tested on RHEL 10.2 (Coughlan)
Package git 2.52.0
Applies to Any host with Git installed
Privilege Normal user
Scope Unstage command reference before commit — git restore --staged, git reset HEAD, bare git reset, directory pathspecs, and git rm --cached. Covers staged deletions and repos with no commits yet. Not a step-by-step undo git add walkthrough or a discard/reset tutorial.
Related guides Git undo add
Git restore examples
Git reset examples
Remove file from tracking
Git diff examples

Unstaging removes paths from Git's staging area (the index) without touching the file content on disk. You use it after git add when a path should not appear in the next commit yet.

This page is the command reference — syntax, flags, pathspecs, and comparisons. If you accidentally ran git add and want the smallest fix with git status -sb explained line by line, start with Git undo add.


Unstage command quick reference

Goal Command
Unstage one tracked file git restore --staged file
Same job (older/common alternative) git reset HEAD file
Unstage several paths git restore --staged file1 file2
Unstage everything under a folder git restore --staged src/
Unstage all staged paths in the repo git reset
Unstage under the current directory only git restore --staged . (run from repo root to cover the whole tree)
Unstage before the first commit git reset path
Unstage a staged deletion git restore --staged file
Unstage a newly added file git restore --staged file
Stop tracking while keeping the file locally git rm --cached file

None of these commands delete untracked files from your working tree by themselves.


Check what is staged

Before you pick a command, confirm which column in git status -sb holds the staging signal. The first column is the index; the second is the working tree.

bash
git status -sb
output
## main
M  app.txt
A  config.env
M  notes.txt
M  src/helper.py

M or A in column one means the path is staged. config.env is a new file staged for the next commit; the others are modifications to tracked files.

To read the staged diff itself:

bash
git diff --staged

That shows what would land in the commit if you ran git commit right now. For a deeper diff walkthrough, see Git diff examples.


Unstage one file with git restore --staged

git restore --staged resets the index entry for one path toward HEAD. Your edits stay on disk.

With app.txt staged among other paths, remove only that file from the index:

bash
git restore --staged app.txt

Confirm column one no longer marks app.txt:

bash
git status -sb
output
## main
 M app.txt
A  config.env
M  notes.txt
M  src/helper.py

The leading space before M on app.txt means it is modified locally but not staged. The other paths are still staged.


Unstage one file with git reset HEAD

git reset HEAD file is the traditional form. On a repository with commits, it unstages the same way as git restore --staged.

Stage app.txt again so you can compare the reset spelling:

bash
git add app.txt

Run the older reset form:

bash
git reset HEAD app.txt
output
Unstaged changes after reset:
M	app.txt

Git prints which paths moved out of the index. Short status should match the restore example:

bash
git status -sb
output
## main
 M app.txt
A  config.env
M  notes.txt
M  src/helper.py

Prefer git restore --staged on Git 2.23 and later because the command name states the intent. git reset HEAD is not deprecated though. Git still documents pathspec reset as the opposite of git add, so keep using it in existing scripts and expect it in older docs.


Unstage multiple files or a directory

List every path in one command when you need several files unstaged but not the whole repo:

bash
git restore --staged app.txt notes.txt

Directory pathspecs work too. After staging src/helper.py with the rest of the tree, unstage the whole folder:

bash
git restore --staged src/

Ask status which paths still sit in column one:

bash
git status -sb
output
## main
M  app.txt
A  config.env
M  notes.txt
 M src/helper.py

Only paths under src/ left the index. Files outside that directory stay staged.


Unstage everything

Two common spellings cover "clear the entire staging area."

Bare git reset (no path) unstages every staged change in the repository:

bash
git reset

Every staged path should now show a space in the first column:

bash
git status -sb
output
## main
 M app.txt
 M config.env
 M notes.txt
 M src/helper.py

Every path now shows a space in column one. Your file edits remain.

git restore --staged . unstages paths that match . — the current directory and everything below it. Run it from the repository root when you mean the whole project:

bash
git add .

From the repository root, . matches the whole tree:

bash
git restore --staged .

Both git reset and git restore --staged . keep working-tree content. The difference is scope: bare reset always targets the full index, while . follows your current directory. For the accidental git add . scenario with status reading first, see Git undo add.


Unstage a new file with git rm --cached

A newly added file can leave the index in two ways.

git restore --staged config.env moves a staged new file back to untracked — same as unstaging a modification.

git rm --cached config.env also removes the path from the index but is the usual choice when you want to stop tracking a file that should stay on disk. Stage the file again to see the cached removal:

bash
git add config.env

The cached form removes the index entry but leaves the file on disk:

bash
git rm --cached config.env
output
rm 'config.env'

Untracked ?? in the last column confirms Git no longer tracks the path:

bash
git status -sb
output
## main
 M app.txt
 M notes.txt
 M src/helper.py
?? config.env

?? means Git no longer tracks config.env, but the file is still present locally. If the file was already tracked in an earlier commit, committing this staged removal drops it from future versions of the repository while your local copy stays on disk. That is the real difference from a simple unstage, which leaves the path tracked — see remove file from tracking.


Unstage a staged deletion

When you git rm a tracked file or stage a manual delete, column one shows D. Unstaging removes the deletion from the index without restoring the file on disk.

A staged delete of the tracked file tracked-del.txt looks like this in short status:

bash
git status -sb
output
## main
D  tracked-del.txt

Unstage the deletion:

bash
git restore --staged tracked-del.txt

Check where the D sits now:

bash
git status -sb
output
## main
 D tracked-del.txt

The space before D means the delete is no longer staged. The file is still missing on disk until you run git restore tracked-del.txt to recover it from HEAD.


Compare git restore --staged, git reset, and git rm --cached

These three names show up together in unstage searches, but they answer different questions:

Command What it changes Typical use
git restore --staged path Index only — working tree untouched Reverse git add on a path you still want tracked
git reset HEAD path or bare git reset Index toward HEAD (and more modes if you add flags) Same unstage result on repos with history; bare form clears all staged paths
git rm --cached path Removes path from index; file stays on disk Stop tracking while keeping the file locally

git reset --hard and git restore --worktree discard working-tree content. They are not unstage commands — see Git discard changes and Git reset examples.


After git commit

Unstage commands apply only while changes sit in the index waiting for a commit. Once git commit finishes, the snapshot is in history and there is nothing left to unstage.

Undo or rewrite that commit with git reset or git revert instead — see Git reset examples and revert to previous commit.


Before the first commit

git restore --staged needs a HEAD commit to compare against. In a freshly initialized repository, it fails:

Start a repository with no commits yet:

bash
git init -b main

Stage a new file in that empty repo:

bash
printf 'new\n' > fresh.txt && git add fresh.txt

git restore --staged fails because there is no HEAD commit to compare against:

bash
git restore --staged fresh.txt
output
fatal: could not resolve HEAD

Use git reset with a path instead:

bash
git reset fresh.txt

The file should return to untracked status:

bash
git status -sb
output
## No commits yet on main
?? fresh.txt

After your first commit, git restore --staged works normally.


Troubleshooting

Symptom Likely cause Fix
fatal: could not resolve HEAD No commits in the repository yet git reset path instead of git restore --staged
Command runs but nothing changes Path was not staged, or typo in the path Run git status -sb and match the path exactly
git reset did not help Changes were already committed Use reset or revert on the commit, not unstage commands
File still shows M after unstage Expected — only column one cleared Edits remain until you commit or discard them
Wanted to discard local edits, not just unstage Unstaging never touches the working tree git restore file rewrites the working file from the index
Wanted to discard both the staged and the unstaged copy Two places hold the change: index and working tree git restore --source=HEAD --staged --worktree file — see Git discard changes
Used git rm --cached by mistake Goal was only to reverse git add git restore --staged file when the file should stay tracked
Staged delete unstaged but file still missing Unstage only removed the index deletion git restore file to recover content from HEAD

References


Summary

Unstaging clears paths from Git's index while leaving your working-tree edits intact. git restore --staged path is the modern default for one file, a list of paths, or a directory pathspec. git reset HEAD path and bare git reset remain valid — especially in scripts — and bare git reset is the fastest way to empty the entire staging area.

git rm --cached belongs in a different column: it stops tracking a file while keeping it on disk, not merely reversing an accidental git add. Staged deletions use the same unstage command as modifications; recovering the file content is a separate git restore step afterward.

For a mistake-driven walkthrough that reads git status -sb before each fix, use Git undo add. To discard edits or rewrite commits after the staging step is past, see Git discard changes and Git reset examples.


Frequently Asked Questions

1. How do I unstage a file in Git?

Run git restore --staged file to reset that path in the index toward HEAD while keeping your working-tree edits. git reset HEAD file does the same job on repositories that already have commits.

2. How do I unstage all files in Git?

Bare git reset unstages every staged path in the repository. From the repository root, git restore --staged . unstages paths under the current directory. Both leave file edits on disk.

3. Does unstaging delete my changes?

No. Unstaging only moves the index entry back toward HEAD. Your modified file content stays in the working tree until you commit, stage again, or discard it.

4. What is the difference between git restore --staged and git reset HEAD?

For unstaging tracked paths on a repo with commits, both commands achieve the same result. git restore --staged names the job clearly. git reset HEAD is the older form that many scripts still use.

5. When should I use git rm --cached instead of git restore --staged?

Use git restore --staged when you only want to reverse git add on a path you still intend to track. Use git rm --cached when the goal is to stop tracking the file while keeping it on disk.

6. Can I unstage files after git commit?

No. Once the commit exists, those changes are no longer a staging-area problem. Undo or rewrite the commit with git reset or git revert instead.

7. Why does git restore --staged fail with could not resolve HEAD?

git restore --staged compares the index to HEAD. In a brand-new repository with no commits yet, HEAD does not exist. Use git reset path to unstage instead.
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)