Post
Git from zero to hero: undoing mistakes
Part 3 of the Git guide: naming commits (HEAD~1, HEAD^2, HEAD@{2}), detached HEAD, and the right undo for each mistake: restore, amend, reset, revert and reflog.
Every undo in Git names a commit, so this part starts with how to point at one, using the recipe book’s history from part 2.
Pointing at commits
Anywhere Git wants a commit, you can give it any of these:
| You write | It means |
|---|---|
9c1d4e2 |
a commit by its (short) hash |
main, pizza |
the commit a branch points at |
v1.0 |
the commit a tag points at |
HEAD |
the commit you’re on |
HEAD~1 (or HEAD^) |
one commit before HEAD, its parent |
HEAD~3 |
three commits back along the chain |
HEAD^2 |
the second parent of a merge commit |
HEAD@{2} |
where HEAD was two moves ago (from the reflog) |
So git diff HEAD~2 HEAD compares now with two commits ago, and git show main~1 shows the commit before the tip of main.
Detached HEAD. Normally HEAD points at a branch. Check out a commit directly, say with git switch --detach 3f2a1b9, and HEAD points straight at the commit. It’s a fine place to look at old code, but commits you make there belong to no branch and are easy to lose. To keep them, run git switch -c keep-this. To leave, git switch main.
Undoing mistakes
The old git checkout did most of these jobs, which confused everyone. Since Git 2.23, git switch moves between branches and git restore handles files. The right tool depends on where the mistake is.
You edited a file and want the last committed version back:
git restore pasta.md # ⚠️ throws away your edits to this file
Add --source=<commit> to get the file as it was in an older commit.
You staged the wrong file:
git restore --staged todo.md
You made a typo in the last commit message, or forgot a file:
git add forgotten.md
git commit --amend # replaces the last commit
You want to undo the last commit but keep the work. git reset moves the branch label back. Three modes decide what happens to the changes that were in that commit:
git reset --soft HEAD~1 # undo the commit, changes stay staged
git reset HEAD~1 # same, changes stay unstaged (--mixed is the default)
git reset --hard HEAD~1 # ⚠️ undo the commit and delete the changes
The commit is already pushed. Don’t rewrite shared history. Add a new commit that cancels the old one:
git revert 9c1d4e2
Rule of thumb: commits only on your computer may be rewritten (reset, amend, rebase); if anyone else might have them, use revert.
You started working on main by mistake. git switch -c new-branch creates a branch where you are, and your uncommitted changes come with you.
You think you lost something. Almost nothing is lost. git reflog lists every place HEAD has been, even after a hard reset, a rebase or deleting a branch:
git reflog
git switch -c rescue 9c1d4e2 # put a branch on the commit you thought was gone