Git Reset
The git reset command is used to undo commits, unstage files, or discard changes. It moves the HEAD pointer (and the current branch pointer) back to a previous commit, effectively "undoing" work.
Important: git reset modifies the commit history. It is primarily meant for undoing changes that have NOT been pushed to a shared remote repository. Using it on public commits can cause problems for collaborators.
The Three Modes of git reset
Git reset has three different levels of "undoing," controlled by flags:
| Mode | What Changes | Working Directory | Staging Area | Commits |
|---|---|---|---|---|
--soft | Moves HEAD back | Unchanged | Unchanged (files remain staged) | Removed from history |
--mixed (default) | Moves HEAD back | Unchanged | Cleared (files become unstaged) | Removed from history |
--hard | Moves HEAD back | Files reverted to that commit | Cleared | Removed from history |
Understanding the Starting State
Commits: A ──── B ──── C ──── D (HEAD)
If git reset HEAD~1 is run, HEAD moves back to C, and the effects on files depend on the mode used.
--soft Reset
Moves HEAD back to the specified commit. The files still exist exactly as they were, AND the changes from the undone commits are still staged.
git reset --soft HEAD~1Use case: "I committed too early. I want to undo the last commit but keep all changes staged so I can recommit with better changes or a better message."
# Before: Committed a file but want to re-commit with a better message
git log --oneline
# a3f92b1 bad commit message
# b7c3d45 Previous commit
git reset --soft HEAD~1
git log --oneline
# b7c3d45 Previous commit
# (a3f92b1 is gone)
git status
# Changes to be committed:
# modified: index.html (still staged!)
git commit -m "Better, descriptive commit message"
--mixed Reset (Default)
Moves HEAD back and clears the staging area. The files are modified in the working directory but need to be re-staged.
git reset HEAD~1
# (same as: git reset --mixed HEAD~1)
Use case: "I committed too early and want to undo the commit, unstage the changes, and decide what to stage and commit again."
git reset HEAD~2
# Moves back 2 commits
# Files are in working directory but NOT staged
--hard Reset
Moves HEAD back and completely discards all changes — both staged and unstaged. The files revert to the state of the target commit.
git reset --hard HEAD~1Warning: A --hard reset permanently deletes uncommitted changes. This cannot be undone (unless the commit hash is known from git reflog).
Use case: "I made a mess of the last few commits. Throw all of it away and go back to a clean state."
# Completely throw away the last 3 commits and all their changes
git reset --hard HEAD~3
# Go back to a specific commit (all changes after that commit are lost)
git reset --hard a3f92b1
Resetting to the Remote Branch (Discard All Local Changes)
To completely discard all local commits and return to the state of the remote branch:
git reset --hard origin/mainThis is useful when local experiments need to be discarded and a fresh start from the remote state is needed.
Unstaging a File with git reset
To remove a file from the staging area (move it back to "modified, not staged") without touching the working directory changes:
git reset HEAD index.htmlOr the modern equivalent:
git restore --staged index.htmlRecovering from an Accidental --hard Reset
Even after a hard reset, commits can often be recovered using git reflog. The reflog keeps a record of every position HEAD has been at:
git reflog
# a3f92b1 HEAD@{0}: reset: moving to HEAD~3
# b7c3d45 HEAD@{1}: commit: Add feature
# c1d2e3f HEAD@{2}: commit: Update styles
# Recover the lost commit
git reset --hard b7c3d45
git reset vs git revert
| Feature | git reset | git revert |
|---|---|---|
| Modifies history | Yes — removes commits | No — adds a new commit to undo changes |
| Safe for shared branches | No | Yes |
| When to use | Local/private branches | Shared/public branches |
Summary
git reset undoes commits by moving HEAD backward. Use --soft to keep changes staged, --mixed (default) to unstage changes, and --hard to discard changes entirely. Only use reset on local, unpushed commits. For undoing commits that are already on a shared remote branch, use git revert instead.
