You ran git add, and now you want the file back out of the staging area — without touching its content, without rewriting history, without gambling on whether the wrong command just wiped your edit. undo git add almost always means one thing: unstage the file. The short answer is git restore --staged <file>, which moves the file out of the index and leaves the edit sitting right where you left it, in the working tree. This guide covers every real shape that question takes — one file, every file, a file you deleted, a Git version too old for restore, a brand-new repo with no commits yet, and the one lookalike command, git rm --cached, that does something different on purpose. If what you actually want is to throw the edit away entirely rather than just unstage it, that's a separate operation covered in Diff Checker's guide to discarding local changes — this article never deletes content, only moves it between Git's three areas.

Quick Answer: How to Undo git add

Every git unstage question — sometimes phrased as git cancel add — comes down to picking the right row below: what you staged, how much of it, and which Git version you're running. git restore --staged <file> is the modern default; git reset HEAD <file> is the classic form that has worked in every Git release since Git 1.0. Neither one touches file content — both only move an entry between the index and HEAD.

# Unstage one file — recommended, Git 2.23+ (August 2019)
git restore --staged <file>

# Unstage one file — classic syntax, works on every Git version
git reset HEAD <file>

# Unstage everything staged
git restore --staged :/
git reset
Situation Command What Changes Destructive? Min Git Version
Unstage one file, keep the edit git restore --staged <file> Index only No 2.23 (Aug 2019)
Unstage one file, classic syntax git reset HEAD <file> Index only No Any
Unstage everything git restore --staged :/ or bare git reset Index only No 2.23+ / Any
Unstage a staged deletion git restore --staged <file> Index only — file stays missing on disk No 2.23+
Unstage part of a file git restore --staged --patch <file> Index, selected hunks only No 2.23+
No commits yet (unborn branch) git reset -- <file> or git rm --cached <file> Index only No Any
Stop tracking the file for good git rm --cached <file> Index now and every future commit Semi — untracks going forward Any
Already committed the add git reset --soft HEAD~1 Moves HEAD, unstages the commit's changes Rewrites history (safe if unpushed) Any
What Do You Want to Unstage? What did you stage? One file Everything A staged deletion Part of a file / specific hunks git restore --staged <file> git restore --staged :/ git restore --staged <file> git restore --staged --patch <file> or git reset HEAD <file> or bare git reset stays missing on disk or git reset --patch All four paths only ever move the index — none of them touch the working tree A staged deletion still needs git restore <file> afterward to bring content back
Match what you staged to the command that unstages it — all four paths only ever move the index, never the working tree.

The same request shows up under a lot of different names. git unstage, git cancel add, git undo git add, git undo add file, git undo added files, reverse git add, git revert add, and the plain opposite of git add are all the same intent phrased differently — none of them are separate Git commands, and none of them require anything beyond restore --staged or reset HEAD. A second cluster drops the space entirely: git unadd, git unadd files, git un add files, and git unadd one file — again, not real Git subcommands, just the plain-English name for unstaging. A third cluster describes the action instead of naming it: git add remove, git add git remove, remove files from git add, git remove added files, and git remove from added all mean "take this file out of what git add just staged." Every one of them resolves to the same two commands above — the only exception is how to cancel git add and commit, which describes committing after adding, covered separately in the already-committed section below, since by then the fix is a different command entirely.

Where Undo Happens: Working Tree, Index, HEAD

git add copies a file's current content from the working tree into the index — the staging area, the snapshot Git will use for the next commit. Every unstage command in this guide runs that copy backward: it overwrites the index entry with whatever HEAD already has, discarding the staged snapshot. The opposite of git add, in that literal sense, isn't one dedicated command — it's git restore --staged (or git reset HEAD), because add's direction is working tree → index, and undoing it means index ← HEAD.

Two Arrows Into the Index, From Opposite Sides git restore --staged <file> git reset HEAD <file> HEAD last commit already saved Index staging area next commit's snapshot Working Tree files on disk your edit lives here git add Neither unstage command draws an arrow to the working tree — only the index moves
git add copies working tree → index. Unstaging reverses only that one step, copying HEAD → index — the working tree side of the diagram never enters the picture.

That's the reassurance worth stating plainly, because it's the real question behind most undo git add searches: unstaging never touches file content on disk. Your edit, your new lines, your deleted lines — none of it changes when you run git restore --staged or git reset HEAD. Only the index entry moves. Run git status immediately after and the file reappears under "Changes not staged for commit" instead of "Changes to be committed" — same content, different bucket. If you actually want the content gone too, that's an entirely different, genuinely destructive operation, covered in full in the git reset --hard guide, and it's worth reading the difference twice before copying a reset command from memory.

git restore --staged: The Modern Way to Unstage

git restore shipped in Git 2.23, released August 2019, specifically to split "restore files" away from the overloaded git checkout command. Its --staged flag (short form -S), documented in Git's official git restore reference, is the direct, modern answer to undo git add: it resets the index entry for a path back to what HEAD has, and stops there.

# Unstage a single file
git restore --staged <file>

# Short flag, same result
git restore -S <file>

# Unstage every staged file in the repo (not just the current directory)
git restore --staged :/

# Unstage everything from the current directory down only — narrower than :/
git restore --staged .

That last distinction matters if you run the command from inside a subdirectory: . scopes to the current directory downward, while :/ covers the whole repository regardless of where you're standing. Confusing the two is an easy way to think you unstaged everything when part of the tree is still staged.

git reset HEAD: The Classic Way to Unstage

Before Git 2.23, git reset HEAD <file> was the only way to git unstage a file, and it still works identically today — Git doesn't remove working commands. Given a path, reset behaves like --mixed unconditionally: it moves the index entry back to HEAD and never touches the working tree, regardless of which mode flag you'd normally reach for — a rule stated explicitly in Git's official git reset reference.

# Unstage one file — works on every Git version ever released
git reset HEAD <file>
git reset -- <file>

# Unstage everything staged
git reset
git reset HEAD
$ git reset HEAD file.txt
Unstaged changes after reset:
M	file.txt

reset and restore --staged produce the identical end state for this case; which one you reach for is a matter of which Git version you're running and which syntax your fingers already know, not a difference in outcome.

Which Git Version Do You Need?

git restore, including its --staged and --patch flags, requires Git 2.23.0, released August 16, 2019, alongside git switch — both were carved out of git checkout's overloaded behavior. Run git --version if you're not sure which side of that line you're on.

git --version
# git version 2.23.0 or newer → git restore --staged works
# older than 2.23              → use git reset HEAD <file> instead

git reset HEAD <file> has no such cutoff — it has worked since Git's earliest releases and needs no version check at all. On a shared CI image, an older Linux distro's default package, or a machine you don't control, reset HEAD is the safer default to script against; restore --staged is the one worth using in your own terminal once you know your Git is current.

Which Unstage Command Works on Your Git Version Git's early releases Git 2.23 (Aug 2019) Today git reset HEAD <file> — works everywhere git restore --staged <file> — from here on Before 2.23, only the reset form exists — restore isn't there yet to fail differently
git reset HEAD <file> works on every Git release. git restore --staged <file> only works from Git 2.23 onward (August 2019).

Unstage One File vs Everything

Both commands scale from a single path to the whole repository — the only thing that changes is the target.

# One file
git restore --staged src/api/client.ts
git reset HEAD src/api/client.ts

# Multiple named files
git restore --staged src/api/client.ts src/api/types.ts

# Everything staged, whole repo
git restore --staged :/
git reset

# Everything staged, current directory down only
git restore --staged .

A bare git reset (no path, no --hard/--soft) always means "unstage everything and stop" — it's the most common way people git unstage all without typing the longer restore --staged :/ form. Verify either version worked with a quick git status: staged entries move from "Changes to be committed" to "Changes not staged for commit," and a fully unstaged tree shows nothing under the first heading at all.

Unstaging a Deleted File (It Won't Reappear)

git add doesn't just stage new content — running it after rm <file> (or after deleting a file in your editor) stages the deletion itself. Unstaging that deletion is where most guides go quiet, and it has a real gotcha: undoing the stage does not bring the file back.

$ rm file.txt
$ git add file.txt
$ git status --short
D  file.txt

$ git restore --staged file.txt
$ git status --short
 D file.txt

Notice the status line moved from D   (staged deletion) to  D (unstaged deletion) — the D shifted columns, but it's still there. file.txt is still missing on disk; only its removal is no longer staged for the next commit. git reset HEAD file.txt lands in the identical spot and even prints Unstaged changes after reset: D  file.txt to confirm it. Neither command inspects the working tree at all — they only ever touch the index — so a missing file stays missing until you explicitly ask for it back:

# Bring the file's content back to disk too, after unstaging the deletion
git restore file.txt

# Or in one step — unstage and restore the working tree copy together
git restore -SW file.txt

That two-step shape — unstage first, then separately restore the working tree — is exactly the model from the earlier section: git add moved the deletion working tree → index, and undoing it one arrow at a time means the working tree arrow needs its own command.

Unstaging a Deleted File Is a Two-Step Recovery git status --short D file.txt Index: deletion staged (gone from next commit) Disk: file absent staged deletion restore --staged git status --short D file.txt Index: matches HEAD (file back, unstaged) Disk: still absent unstaged deletion still needs git restore <file> git status --short (clean) Index: matches HEAD Disk: file restored content is back Unstaging a deletion never restores content — that's a second, separate command git restore -SW file.txt runs both steps at once
Unstaging a staged deletion moves it from "staged" to "unstaged" — the file stays missing on disk until you separately run git restore on the working tree.

Unstage Part of a File: Patch Mode

git add -p famously stages a file hunk by hunk. Both unstage commands have a matching interactive flag for undoing exactly that — pulling specific hunks back out of the index while leaving the rest of the file staged.

# Modern syntax, Git 2.23+
git restore --staged --patch <file>
git restore -S -p <file>

# Classic syntax, works on every Git version
git reset --patch <file>
git reset -p <file>

Either command walks the staged hunks in the file one at a time and asks Unstage this hunk [y,n,q,a,d,e,?]?y unstages that hunk, n leaves it staged, s tries to split it into smaller hunks, and e opens a manual edit if the automatic split isn't fine-grained enough. Nothing about patch mode touches the working tree; a hunk you unstage this way simply becomes an unstaged edit in the same file, sitting right next to whatever hunks are still staged.

Fresh Repo, No Commits Yet: When HEAD Doesn't Exist

On a brand-new repository — git init, one git add, zero commits — both standard unstage commands fail, because both resolve against HEAD, and HEAD doesn't point anywhere yet. Verified directly, against Git 2.50.1:

$ git init demo
$ cd demo
$ echo "hello" > file.txt
$ git add file.txt
$ git reset HEAD file.txt
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

$ git restore --staged file.txt
fatal: could not resolve HEAD

Two different error messages, same underlying cause — neither command has a commit to reset the index against. This is worth flagging precisely because it's easy to assume git restore --staged is the safe universal answer; on an unborn branch it fails just like reset HEAD does, only with different wording. Two commands sidestep the problem entirely, because neither one needs a resolvable HEAD:

# Works on an unborn branch — no revision argument at all, just a pathspec
git reset -- file.txt

# Works too — removes the path from the index directly
git rm --cached file.txt

git reset -- <file> works specifically because -- tells Git that everything after it is a path, not a revision — with no revision token to resolve, Git never tries to look up HEAD and simply clears the path from the index. git rm --cached <file> sidesteps the issue a different way: it never consults HEAD at all, it just removes the entry from the index directly. Once that first commit exists, this entire edge case disappears — git reset HEAD <file> and git restore --staged <file> both work normally from the second commit onward.

git rm --cached: A Different Command Entirely

git add remove, git add git remove, and git remove added files often lead people here — and it's worth being precise about what git rm --cached actually does, because it's not a synonym for unstaging. git rm --cached <file> removes the file from the index and stops Git from tracking it going forward, while leaving the file itself untouched on disk. That's a different intent from unstaging: unstaging says "not in this commit, but still tracked"; rm --cached says "stop tracking this file, permanently, starting now."

# Stop tracking a file, keep it on disk (e.g. you just realized .env was staged)
git rm --cached .env

# Same, recursively for an entire directory
git rm -r --cached node_modules

# Committing after this makes the "stop tracking" permanent
git commit -m "Stop tracking .env"

The case where this distinction actually matters: you ran git add ., it swept up a file that should never be tracked at all — a .env, a build directory, a local config — and simple unstaging isn't enough, because the very next git add . will just stage it again. git rm --cached combined with adding the path to .gitignore is the actual fix; git restore --staged alone only buys you until the next add.

Unstage Wildcards and Whole Directories

Both commands accept any pathspec Git understands — globs, directories, multiple arguments at once, not just a single filename.

# Unstage every .log file staged anywhere in the repo
git restore --staged "*.log"

# Unstage everything staged under one directory
git restore --staged src/

# Same, classic syntax
git reset HEAD src/

# Multiple patterns at once
git restore --staged "*.log" "*.tmp" build/

Quote glob patterns like "*.log" so your shell passes the literal asterisk to Git instead of expanding it against files in the current directory first — an unquoted *.log only matches files that already exist in your current working directory, which silently misses staged deletions and files sitting in subdirectories.

Unstaging in VS Code, GitHub Desktop, Sourcetree, GitKraken, JetBrains

Every GUI Git client wraps the same two commands from above; the button labels differ, the underlying operation doesn't.

  • VS Code — Source Control panel, hover a file under "Staged Changes," click the minus (−) icon to unstage it, or click "Unstage All Changes" at the top of that group.
  • GitHub Desktop — uncheck a file's checkbox in the Changes list to unstage it before committing; there's no separate confirmation step.
  • Sourcetree — select one or more files in the "Staged files" pane and click Unstage, or drag them back down into the "Unstaged files" pane.
  • GitKraken — click a staged file in the commit panel to move it back to "Unstaged Files," or use the "Unstage All" link above the list.
  • JetBrains IDEs (IntelliJ, WebStorm, PyCharm) — in the Commit tool window, uncheck a file to unstage it, or right-click and choose "Rollback" only if you actually mean to discard the edit — that button is destructive, unlike the checkbox.

That JetBrains distinction is worth a second look: "unstage" and "rollback" sit next to each other in the same context menu, and only one of them is reversible in the sense this whole guide has been describing. Before comparing a staged file against its committed version in any of these tools, pasting both versions into a dedicated diff view makes the actual content difference obvious before you click anything.

Already Committed? Undo git commit Instead

how to cancel git add and commit describes a different moment than everything above — the add already happened, and so did the commit. At that point there's no staged entry left to unstage; the change is sitting in a commit, and undoing it is a commit-level operation, not an index-level one.

# Undo the last commit, keep the changes staged
git reset --soft HEAD~1

# Undo the last commit, keep the changes unstaged (back to a plain edit)
git reset --mixed HEAD~1

# Undo the last commit AND its content — destructive, use with care
git reset --hard HEAD~1

--soft is the closest analogue to "undo the add and the commit but keep everything staged" — it moves HEAD back one commit and leaves the index untouched, so the files show up right back under "Changes to be committed," exactly where they were before you ran git commit. The full mechanics, including what to do if the commit was already pushed, are in the dedicated guide to undoing the last commit. If the commit itself is fine and only its message or a small addition needs fixing, git commit --amend is the narrower tool — no history rewind needed. And if you'd rather set the whole thing aside instead of deciding right now, git stash shelves staged and unstaged changes alike onto a recoverable stack without touching the commit at all. A pile of rough checkpoint commits made while sorting this out can be squashed into one afterward, so committing early to be safe doesn't cost you clean history later.

git restore vs git reset vs git rm --cached vs git revert

Four commands, four different jobs, and mixing them up is the single most common way someone means to unstage a file and ends up either deleting content or misunderstanding what "revert" even applies to here. git restore --staged and git reset HEAD unstage — index only, and content never leaves the working tree either way. git rm --cached untracks — index now and every future commit, working tree untouched, but the file will vanish from the repo the moment you commit that change. git revert doesn't apply to this problem at all: it creates a new commit undoing a previous commit, and a staged-but-uncommitted git add was never a commit to begin with — git revert add is a common way to phrase "undo git add," but the command it names does something else entirely. The table below lines up what each command touches, how destructive it is, and when you'd actually reach for it.

Command What It Touches Touches Working Tree? Destructive? Typical Use
git restore --staged <file> Index only No No Unstage a file, modern syntax
git reset HEAD <file> Index only No No Unstage a file, classic syntax
git rm --cached <file> Index now and every future commit No Semi — untracks going forward Stop tracking a file entirely
git revert <commit> Creates a new commit undoing a prior commit Only via the new commit it creates No — adds history, doesn't remove it Undo a commit already shared with others
Four Commands, Four Different Jobs git restore --staged git reset HEAD git rm --cached git revert What it touches Index only Index only Index + future commits New commit, undoes old one Touches working tree? No No No Only via the new commit Destructive? No No Semi — untracks it No — adds history Typical use Unstage, modern syntax Unstage, classic syntax Stop tracking for good Undo a shared commit restore and reset only ever touch the index — rm --cached and revert reach further
restore --staged and reset HEAD only ever touch the index; rm --cached also reaches every future commit; revert never touches the index or working tree at all — it just adds a new commit undoing an old one.

Verify: git status and git diff --staged

Two commands confirm an unstage did what you expected, before you commit anything else.

# Confirms which bucket each file is in right now
git status

# Shows exactly what's still staged — empty output means nothing is staged
git diff --staged
git diff --cached   # identical, older alias

# Shows what's changed but NOT staged — this is where an unstaged edit lands
git diff

git diff --staged is the one worth running right after any unstage: if it prints nothing, the index is clean and matches HEAD exactly. If a file you meant to fully unstage still shows up there, you likely only unstaged part of it in patch mode, or targeted the wrong path. For a closer look at a specific staged hunk than the terminal's plus/minus output gives you, pasting the git diff --staged output for one file next to the plain git diff output for the same file into a side-by-side diff view makes it obvious at a glance whether the staged and working copies have actually diverged, or whether they're identical and the unstage is complete.

Frequently Asked Questions

What's the difference between git reset and git restore for unstaging a file?

For unstaging specifically, none — git reset HEAD <file> and git restore --staged <file> produce the identical result: the file's index entry is reset to match HEAD, and the working tree is untouched either way. The real difference is scope and Git version. git reset without a path can also move branches and rewrite history — that's the destructive form people confuse with the harmless one. git restore only ever restores files and requires Git 2.23 (August 2019) or newer; reset has no version requirement at all.

How do I undo git add before I commit?

Run git restore --staged <file> (or git reset HEAD <file> on older Git) for one file, or git restore --staged :/ (or a bare git reset) to unstage everything. Either one moves the file back to "Changes not staged for commit" — your edit stays exactly as it was, just no longer scheduled for the next commit.

Can I undo git add for one file only?

Yes. Both git restore --staged <file> and git reset HEAD <file> take a single path and only touch that file's index entry — every other staged file stays staged exactly as it was. Name multiple paths to unstage a specific subset, or use --patch on either command to unstage individual hunks within one file while leaving the rest of that same file staged.

How do I remove files from the staging area?

"Remove files from git add" and "unstage" name the same operation, because the staging area is the index. Use git restore --staged <file> for one path, git restore --staged :/ for everything staged, or git reset HEAD <file> on Git older than 2.23. Nothing leaves your disk — the entries move from "Changes to be committed" back to "Changes not staged for commit." The one case that needs a different command is a file that should never have been tracked at all, such as a .env: there, git rm --cached <file> plus a .gitignore entry is the real fix, because unstaging alone only holds until the next git add .

How do I cancel git add and commit if I already committed?

Unstaging doesn't apply anymore at that point — the change is inside a commit, not the index. git reset --soft HEAD~1 undoes the last commit and puts its changes right back into the staging area, closest to the original "just add, don't commit" state. git reset --mixed HEAD~1 does the same but leaves the changes unstaged instead. Never run --hard for this unless you genuinely want the content gone too — it deletes the commit's changes from the working tree as well.

Does git rm --cached delete the file from my computer?

No — that's the entire point of the --cached flag. git rm --cached <file> removes the file from Git's index (so it's no longer tracked and won't be included in the next commit) while leaving the actual file untouched on disk. Running plain git rm <file> without --cached is the destructive version — it stages a deletion and removes the file from your working tree in the same step.