Undoing Things in Git
One of the best things about Git is that almost nothing is truly permanent: there's a way to undo almost any mistake. This post walks through the most common "undo" scenarios, from a typo in your last commit message to rewriting your entire commit history.
1. Fixing the Last Commit
If you've just committed and realize you forgot to include a file, or made a typo in the commit message, you don't need a brand-new commit to fix it. Use
--amend:git commit --amend
This reopens your editor so you can update the commit message. If you only want to add the staged changes without touching the message, skip the editor with
--no-edit:git commit --amend --no-edit
Key rule: never amend a commit that's already been pushed. Amending doesn't edit the old commit: it creates a brand-new one with a new hash and replaces the old one. If anyone else has already pulled the original commit, your amended version will conflict with their history and cause confusion.
2. Unstaging a File
Before looking at how to undo changes, it helps to understand Git's three areas: the Working Directory (your local files), the Staging Area (files marked to be committed), and the Local Repository (committed history). The restore commands help you move files back through these stages.
!The Three Areas of Git: Working Directory, Staging Area, and Local Repository
Added something to the staging area by mistake?
git restore --staged removes it from the staging area without touching your actual changes. They stay right where they are in your working directory:git restore --staged <filename>
3. Discarding Changes to a File
If you want to throw away your edits to a file entirely and go back to the last committed version, use:
git restore <filename>
Be careful with this command: it permanently discards any uncommitted changes to that file. There is no way to recover them afterward.
4. Restoring a File from an Older Commit
Sometimes you don't want to discard a file's changes: you want to bring back an older version of it from a specific commit:
git checkout <commit-id> -- path/to/file
This pulls that file's contents from the given commit into your working directory, without affecting any other files or your commit history.
5. Undoing a Commit: revert vs. reset
This is where most of the confusion happens, so let's go through both options carefully.
git revert
git revert doesn't delete or replace anything. Instead, it creates a new commit that undoes the changes from a previous one. If a commit added a line, the revert commit removes that same line: your history moves forward, but the code ends up back where it was.!git revert: a new commit D is appended to undo the changes of commit C
git revert <commit-id>
Because it adds to history instead of rewriting it,
revert is the safer choice for undoing commits that have already been pushed or shared with others.git reset
git reset takes a different approach: it moves your current branch pointer (HEAD) back to an earlier commit.!git reset: the HEAD pointer moves backward from C to B, orphaning C
What happens to the changes in between depends on which mode you use:
git reset --soft: movesHEADback, but keeps all the changes from the undone commits staged and ready to commit again. Use this when you want to combine or redo recent commits without losing any work.git reset --mixed(the default): movesHEADback and unstages the changes, but leaves them sitting in your working directory. This is the middle ground: nothing is staged, but nothing is lost either.git reset --hard: movesHEADback and throws away everything: staged changes and working directory changes alike. Use this with real caution, since the discarded changes are not recoverable.git reset --keep: similar to--hardin that it discards the commits, but it tries to preserve any uncommitted local changes you currently have, and will abort if doing so would overwrite them. It's a safer alternative when you have uncommitted work you don't want to lose.
As a rule of thumb: reach for
revert on shared branches, and save reset --hard for commits and branches that are still entirely local to you.