Developer Tools

How to Undo git add (Before Commit, Keep Changes)

Published Nov 15, 2026
Tested on a real Mac. Every command was run in throwaway repos with Apple Git 2.39.5 on macOS 26.6.2; the interactive git reset -p / git restore -p prompts were not run. Test machine: MacBook Air (M3, 16 GB), macOS Tahoe 26.6.2, zsh 5.9.
On this page
  1. Quick reference
  2. Unstage one file with git restore —staged
  3. Unstage everything (undo git add .)
  4. Gotcha: before the first commit, git restore —staged fails
  5. Don’t use git rm —cached on files that are already committed
  6. What happens if you edited the file again after git add
  7. Insight: the staged copy stays inside .git (and can be recovered)
  8. Adding a file to .gitignore does not unstage it
  9. Common questions

To undo git add for one file before you commit, run:

git restore --staged app.txt

To unstage everything you added (for example after git add .), run git reset with no arguments. Both commands only change what is staged. Your edits in the working folder stay exactly as they are, so nothing is lost.

Quick reference

You want to…Command
Unstage one filegit restore --staged <file>
Unstage all filesgit restore --staged . or git reset
Unstage in a repo with no commits yetgit reset <file> or git rm --cached <file>
Older syntax (still works)git reset HEAD -- <file>
Undo git add and the commitSee Git undo last commit

I tested all of these with the Git that ships with the Xcode Command Line Tools:

git version 2.39.5 (Apple Git-154)

git restore has been in Git since 2.23, so Apple’s Git has it. You do not need Homebrew Git for anything here.

Unstage one file with git restore —staged

My test repo had one commit. Then I edited app.txt, created .env and notes.txt, and ran git add . by mistake. git status even prints the undo command for you:

On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   .env
	modified:   app.txt
	new file:   notes.txt

To take only .env out of the next commit:

git restore --staged .env
git status -s
M  app.txt
A  notes.txt
?? .env

.env went back to untracked (??). The other two files are still staged. The file itself is untouched on disk.

Unstage everything (undo git add .)

You have two equivalent options:

git restore --staged .

or the shorter classic:

git reset

Here is what git reset printed in my repo:

Unstaged changes after reset:
M	app.txt

It only lists files Git already tracked. The new files (.env, notes.txt) are unstaged too, they just aren’t mentioned. Check with git status -s:

 M app.txt
?? .env
?? notes.txt

Notice the M moved from the first column (staged) to the second column (modified, not staged). Add -q (git reset -q) if you don’t want the message.

Plain git reset is safe. It defaults to --mixed, which never touches your files. Only git reset --hard throws away edits, so don’t add that flag here.

Gotcha: before the first commit, git restore —staged fails

This one tripped me up. In a brand-new repo with no commits, git restore --staged does not work:

git init
echo hi > a.txt
git add a.txt
git restore --staged a.txt
fatal: could not resolve HEAD

There is no commit yet, so there is nothing to “restore” from. Git hints at the right command in this case. Look at the status text — it changes when there are no commits:

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   a.txt

Both of these worked in the empty repo:

git reset a.txt
git rm --cached a.txt

git reset a.txt exited with status 0 and left the file untracked (?? a.txt). git rm --cached a.txt printed rm 'a.txt' and did the same.

Don’t use git rm —cached on files that are already committed

git rm --cached is often suggested as “the” way to undo git add. That is only true for new files. On a file that is already in a commit, it does something very different: it stages a deletion.

git rm --cached app.txt
git status -s
D  app.txt
?? app.txt

If you commit now, app.txt is removed from the repo for everyone (your local copy stays, as untracked). That is the right move when you want Git to stop tracking a file, but it is not “undo add”. If you ran it by accident, git restore --staged app.txt puts things back:

 M app.txt

What happens if you edited the file again after git add

Say you stage app.txt, then keep editing it. Status shows MM: one version staged, newer changes on top.

MM app.txt

Running git restore --staged app.txt does not roll your file back to the staged version. It keeps the newest content on disk and only empties the staging area:

 M app.txt
line one
line two
line three

All three lines are still in the file. Nothing you typed after git add is lost.

Insight: the staged copy stays inside .git (and can be recovered)

When you run git add, Git immediately saves a copy of the file inside .git/objects. Unstaging does not delete that copy. I tested this by staging a file, overwriting it, and then unstaging:

echo "draft v1 important" > draft.txt
git add draft.txt
echo "oops" > draft.txt
git restore --staged draft.txt
git fsck --lost-found
dangling blob e5c5c5583f49a34e86ce622b59363df99e09d4c6
dangling blob 8ba3a16384aacc37d01564b28401755ce8053f51
dangling blob 587cd42914ec039fde0c0037e9e5c42d8f20d017
dangling blob db3dad9f594eeb894b12ea325151f1a5c2cfa942

git fsck --lost-found writes every orphaned copy to .git/lost-found/other/. One of them held the lost text:

.git/lost-found/other/587cd42914ec039fde0c0037e9e5c42d8f20d017:
draft v1 important

So if you staged a version and then lost it in your editor, you can usually get it back this way.

The flip side: the .env file I staged by mistake was also there, as secret=1. Unstaging does not wipe it from .git. It is not pushed anywhere (dangling objects aren’t sent on git push), but if you zip or copy the whole project folder, it goes along. Git’s automatic cleanup only prunes them once they are older than two weeks (the default gc.pruneExpire), and only when that cleanup actually runs. To remove them now:

git gc --prune=now

After that, git cat-file -t db3dad9f returned fatal: Not a valid object name, and git fsck --lost-found found nothing. If you ever need to clean secrets out of commits you already pushed, that is a bigger job than this.

Adding a file to .gitignore does not unstage it

Another thing I checked: if you staged .env and then add it to .gitignore, it stays staged.

A  .env
?? .gitignore

.gitignore only affects untracked files. You still need git restore --staged .env (or git rm --cached .env once it’s committed). If you set up .env files often, our guide on creating files in Terminal shows how hidden dotfiles behave in Finder too.

Common questions

Does undoing git add delete my changes?

No. git restore --staged, git reset and git reset HEAD -- <file> only change the staging area. In every test above, the file on disk was unchanged afterward.

What’s the difference between git restore —staged and git reset?

For unstaging they do the same thing. git restore --staged is the newer, clearer command that Git’s own status hints use. git reset <file> is older and works even before the first commit, where restore --staged fails with fatal: could not resolve HEAD.

How do I unstage only part of a file?

Use git restore --staged -p <file> (or git reset -p <file>). Git shows each staged chunk and asks y or n. I did not run the interactive prompt for this guide, but it is the mirror of git add -p.

I already committed. How do I undo git add now?

Then the add is part of a commit. Run git reset --soft HEAD~1 to undo the commit and keep everything staged, then unstage what you want. The full walkthrough is in Git undo last commit.

How do I see what is staged before I commit?

git diff --cached --name-only lists staged files. In my repo it printed app.txt and notes.txt. Drop --name-only to see the actual changes.