Git offers several distinct ways to undo something — picking the right one depends on exactly what needs undoing, and whether it's already been shared with anyone else.
# Discard changes to one file, back to the last commit
git checkout -- index.html
# or, the newer equivalent:
git restore index.htmlThis one is irreversible
This permanently discards the uncommitted change — there's no undo for an undo here. Worth double-checking git diff first to see exactly what would be lost.
# Move a staged file back to just "modified" — the change itself is kept
git restore --staged index.htmlMoves the current branch pointer backward, with three levels of how much gets undone along with it.
| Reset type | What happens to the changes |
|---|---|
| git reset --soft HEAD~1 | The last commit is undone, but its changes stay staged |
| git reset --mixed HEAD~1 (the default) | The last commit is undone, changes stay in the working folder but unstaged |
| git reset --hard HEAD~1 | The last commit AND its changes are both gone entirely |
The most dangerous command in this lesson
git reset --hard discards work with no recovery through normal means. Never run it without being certain — and never on a commit that's already been pushed and shared with others, covered below.
Rather than erasing a commit from history, revert creates a brand-new commit that undoes its changes — the original commit stays in history, which is exactly what's needed once a commit has already been pushed and others may have it.
git revert a1b2c3d
# Creates a new commit that undoes commit a1b2c3d's changes| Situation | Use |
|---|---|
| An uncommitted change, not staged yet | git restore |
| A commit made locally, never pushed or shared | git reset |
| A commit already pushed and possibly shared with others | git revert — never reset a shared commit |