Unstage a file you added by mistake
How do I undo git add without losing my changes?
Short answer
git restore --staged <file> removes a file from the staging area and leaves your edits completely untouched. The older equivalent is git reset HEAD <file>. Neither one touches the contents of your file, so this is a zero-risk operation.
git restore --staged path/to/file.ts
Unstages the file, keeping every edit you made
Does this match your situation?
- You ran git add . and swept in files you didn't mean to commit.
- A .env, build folder or large binary is now staged.
- You want to commit only part of your work.
Step-by-step fix
See what's currently staged
git status separates staged from unstaged clearly, and git diff --staged shows the exact content you're about to commit. Check before you unstage so you know what you're aiming at.
step 1git status -sb git diff --staged --statUnstage one file, several files, or everything
restore --staged is the modern spelling and reads clearly. The reset form does exactly the same thing and still appears in most older tutorials, so both are worth recognising.
step 2git restore --staged .env # one file git restore --staged src/ assets/ # several paths git restore --staged . # everything # classic equivalent: git reset HEAD .envNeither command modifies your working files — only the staging area changes.
If the file should never be tracked, ignore it properly
Unstaging is a one-time fix. If it's a secret file or build output, add it to .gitignore so it can't be staged again by an absent-minded git add .
step 3echo ".env" >> .gitignore git add .gitignore git commit -m "Ignore local environment file"If you already committed it, that's a different fix
Unstaging only helps before the commit exists. Once committed, you need to amend or reset — and if the file contains a secret and it's been pushed, treat it as leaked.
step 4git reset --soft HEAD~1 # undo the commit, keep changes staged git restore --staged .env # then unstage the offending file git commit -m "Commit without the env file"
Why this works
Git has three places a file can live: your working directory, the index (staging area) and the repository. git add copies working-directory content into the index; committing turns the index into a permanent snapshot. Unstaging simply rewrites the index entry for that path back to whatever HEAD has, which is why your working file is never touched. It's the clearest illustration of why the index exists at all: it lets you compose a commit deliberately instead of recording whatever happens to be on disk.
How to stop it happening again
- Maintain a real .gitignore from day one — use gitignore.io or GitHub's templates for your stack.
- Prefer git add -p over git add . so every hunk is a conscious decision.
- Run git status before every commit; it takes a second and catches almost everything.
Commands used in this guide
git restoreThrow away working-directory changes or unstage files (the modern, clearer replacement for checkout).
git resetMove the current branch pointer, optionally rewriting the index and your files.
git statusShow what's changed, what's staged, and what Git is ignoring.
git addStage changes so they go into your next commit.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
How do I undo git add?
git restore --staged <file> unstages it while leaving your edits intact. The equivalent older command is git reset HEAD <file>. Neither modifies the file's contents, so there is no risk of losing work.
What's the difference between git restore and git restore --staged?
A big and dangerous one. git restore --staged <file> only unstages, keeping your edits. git restore <file> without --staged overwrites your working file with the committed version, permanently discarding your uncommitted changes.
How do I unstage everything at once?
git restore --staged . unstages every path under the current directory, or git reset with no arguments unstages everything in the repository. Both keep your working-directory changes exactly as they are.
Related rescue guides
How do I throw away all my local changes in Git?
git restore . discards edits to tracked files and git clean -fd deletes untracked ones. Both are irreversible for anything that was never staged or committed, so run git stash first if there's any doubt — it takes a second and makes the whole operation undoable.
Read the fix →Always fixableWhy is .gitignore not working for files that are already tracked?
.gitignore only applies to untracked files. Once a file has been committed, Git keeps tracking it no matter what the ignore rules say. Remove it from the index with git rm --cached <file> — that stops tracking while leaving the file on your disk.
Read the fix →Always fixableHow do I undo the last commit but keep my work?
git reset --soft HEAD~1 removes the commit and leaves every change staged, ready to recommit. Use --mixed (the default) if you also want the changes unstaged, and only use --hard if you genuinely want the work destroyed. If the commit is already pushed, revert it instead of resetting.
Read the fix →Always fixableI committed a secret — how do I remove it from Git history?
Rotate the secret first — that's the only step that genuinely protects you. Then purge it from history with git filter-repo (or the BFG Repo-Cleaner) and force-push. Deleting the file in a new commit does nothing, because the old commit still contains it and is still fetchable.
Read the fix →