How to Undo Git Commits and Changes
Mistakes happen during development. Git provides several commands to undo changes at different stages—before committing, after committing, or in specific files. Understanding which tool to use depends on where you are in the workflow.
Revert the entire working tree before committing
If you’ve made changes to multiple files and want to discard everything back to the last commit:
git reset --hard HEAD
This restores all tracked files to their committed state. Untracked files remain untouched—use git clean -f if you want to remove those too.
Warning: --hard is destructive. Once executed, unsaved changes are gone. If there’s any chance you’ll need the changes later, use git stash instead:
git stash
Then retrieve them later with git stash pop.
Revert changes to a particular file before committing
If you’ve modified a file but haven’t staged it yet:
git checkout HEAD -- working_file
Or the shorter form (though less explicit):
git restore working_file
git restore is the modern approach introduced in Git 2.23 and is preferred for clarity—it’s explicit about restoring files rather than switching branches.
Remove a file from the staging area
If you’ve accidentally staged a file with git add:
git reset working_file
The file remains in your working tree with changes intact. Then delete it if needed:
rm working_file
Alternatively, use the modern command:
git restore --staged working_file
Revert changes to a specific file after committing
To restore a single file to its state in a previous commit:
git checkout <commit> -- working_file
git restore --source=<commit> working_file
Replace <commit> with a commit hash, tag, or reference:
git checkout HEAD^ -- working_file # previous commit
git checkout HEAD~3 -- working_file # three commits back
git checkout v1.2.0 -- working_file # specific tag
git checkout main -- working_file # from another branch
The --source syntax is the newer restore equivalent and is clearer about intent.
Undo a commit entirely
To remove a commit and rewrite history (only use on unpushed commits):
git reset --hard HEAD~1
This deletes the last commit. Use HEAD~N to go back N commits.
Undo a commit but keep its changes
If you want to uncommit work without losing it:
git reset --soft HEAD~1
Changes return to the staging area, allowing you to re-commit with corrections.
Revert a commit with a new commit (safe for shared branches)
If the commit is already pushed, create a new commit that undoes it:
git revert <commit-hash>
This opens an editor for the revert commit message. For public branches, this is safer than reset since it preserves history. Example:
git log --oneline # find the commit
git revert abc1234
Recover a deleted file
If you deleted a file but haven’t committed the deletion:
git restore deleted_file
Or the older syntax:
git checkout deleted_file
If you’ve already committed the deletion, restore it from a specific commit:
git restore --source=HEAD~1 deleted_file
Then stage and commit it back.
Clean up untracked files
Untracked files (new files never added to git) can accumulate. Preview what will be removed:
git clean -n
Then remove them:
git clean -f
Use -d to also remove untracked directories:
git clean -fd
Be careful—untracked files can’t be recovered this way.
View and checkout older versions
To see commit history:
git log --oneline
To inspect a specific older version (enters detached HEAD state):
git checkout <commit-hash>
Create a branch from this point if you want to keep working:
git checkout -b recovery-branch
To return to your main branch:
git checkout main
Quick reference
| Goal | Command |
|---|---|
| Discard unstaged file changes | git restore <file> |
| Unstage a file | git restore --staged <file> |
| Undo last commit (unpushed) | git reset --soft HEAD~1 |
| Delete last commit entirely | git reset --hard HEAD~1 |
| Undo pushed commit safely | git revert <hash> |
| Restore file from old commit | git restore --source=<commit> <file> |
| Remove untracked files | git clean -fd |
| Discard all unstaged changes | git restore . |
Always use git status and git diff before destructive operations to confirm what you’re about to change. When in doubt, git stash buys you time to think.

To delete a local commit (not revert, a revert is a commit) that is not pushed to the remote server yet, such as the HEAD:
$ git reset –hard HEAD^
The reset the working repository to one commit before HEAD. Hence, HEAD is “deleted”.