mosaddik.dev
All posts

How to undo mistakes in Git: restore, reset, revert and reflog

Mosaddik Billah 6 min read

Software Engineering

A Git commit history graph with coloured branch lines on a dark screen
Photo by Yancy Min on Unsplash
On this page
  1. First: find out where you are
  2. 1. Throw away changes to a file you haven’t committed
  3. 2. Unstage a file you added by mistake
  4. 3. Fix the last commit’s message or add a forgotten file
  5. 4. Undo the last commit but keep the changes
  6. 5. Delete the last commit and its changes completely
  7. 6. Undo a commit that’s already pushed
  8. 7. You committed to the wrong branch
  9. 8. Get back something you “lost” with reset
  10. 9. Put work aside without committing it
  11. Which command do I need?

Almost every Git mistake can be undone. The trouble is that Git has several “undo” commands, and the right one depends on where the mistake is: in a file you haven’t saved to Git yet, in a commit only you have, or in a commit you’ve already pushed and other people may have pulled.

This guide goes through each case with the exact command to run. Find the one that matches what just happened, and you’ll be back where you wanted in under a minute.

First: find out where you are#

Run this before anything else:

Shell
git status
git log --oneline -5

git status tells you which files are changed, which are staged (added), and which branch you’re on. git log --oneline -5 shows the last five commits. Between them you can tell which of the cases below you’re in.

One rule to keep in mind the whole way through: if a commit has been pushed to a shared branch, don’t rewrite it. Use git revert (below) instead. Rewriting shared history breaks everyone else’s copy.

1. Throw away changes to a file you haven’t committed#

You edited a file, made a mess, and want it back the way it was at the last commit:

Shell
git restore index.html

To throw away every uncommitted change in the project:

Shell
git restore .

This can’t be undone: the edits were never in Git, so Git can’t bring them back. Read git status first. Files you created but never added aren’t touched by restore; delete them yourself, or see git stash below if you might want them later.

2. Unstage a file you added by mistake#

You ran git add . and it picked up a file that shouldn’t go into the next commit (a .env file, a big export, a half-finished change):

Shell
git restore --staged .env

The file keeps your edits; it just isn’t staged any more. If it should never be committed, add it to .gitignore too. For keeping secrets out of a repository, see environment variables in Node.js.

3. Fix the last commit’s message or add a forgotten file#

You committed, then noticed a typo in the message or a file you forgot. If you haven’t pushed yet:

Shell
git add forgotten-file.js        # only if you forgot a file
git commit --amend -m "Add the contact form"

--amend replaces the last commit with a new one that includes whatever is staged and the new message. Without -m, it opens your editor with the old message so you can edit it.

If you already pushed the commit, leave it. A small follow-up commit (“Add the missing file”) is better than rewriting shared history.

4. Undo the last commit but keep the changes#

You committed too early, or to the wrong idea, and want to redo the commit without losing any work:

Shell
git reset --soft HEAD~1

HEAD~1 means “one commit before the current one”. --soft moves the branch back one commit and leaves all the changes staged, exactly as they were just before you committed. Change what you need and commit again.

There’s also the default, git reset HEAD~1 (called --mixed), which does the same but unstages the changes too. Use it when you want to split one commit into several smaller ones.

5. Delete the last commit and its changes completely#

You want the last commit, and everything in it, gone:

Shell
git reset --hard HEAD~1

--hard moves the branch back and makes your files match that commit, discarding the changes. It also wipes any uncommitted edits in your working folder, so run git status first and commit or stash anything you want to keep.

Only do this on commits you haven’t pushed. If you have, use the next section.

6. Undo a commit that’s already pushed#

When a commit is on a shared branch like main, the safe undo is git revert. It doesn’t remove the commit: it adds a new commit that does the opposite.

Shell
git log --oneline -5
# a1b2c3d Change the pricing table   <- the bad one
git revert a1b2c3d
git push

Git opens your editor with a message like Revert "Change the pricing table"; save and close it (or add --no-edit to skip the editor). History stays intact, everyone else can pull normally, and the bad change is gone from the code.

If the revert hits a conflict, because later commits changed the same lines, Git stops and lets you fix the files, then git revert --continue. To give up, git revert --abort.

7. You committed to the wrong branch#

You made a commit on main that should have been on a feature branch. If it isn’t pushed:

Shell
git branch feature/contact-form    # create the branch here, with the commit on it
git reset --hard HEAD~1            # move main back one commit
git switch feature/contact-form    # carry on working on the branch

The new branch points at your commit, so nothing is lost; main just goes back to where it was. For a simple way to avoid this in the first place, see a Git branching workflow for small teams.

8. Get back something you “lost” with reset#

You ran git reset --hard and realise you needed that commit after all. It’s almost certainly still there. Git keeps a log of every place your branch has pointed, called the reflog:

Shell
git reflog
f0c7c8f HEAD@{0}: reset: moving to HEAD~2
6c466d4 HEAD@{1}: revert: Revert "two"
bf9f14b HEAD@{2}: commit: two

Each line is a state you were in. Find the one just before the mistake and go back to it:

Shell
git reset --hard HEAD@{1}

The same trick recovers a deleted branch: find its last commit in the reflog and run git branch the-branch-name <commit>. Reflog entries last for weeks by default, so act soon, but you don’t need to panic.

The reflog only covers commits. Edits that were never committed (case 1) can’t be recovered this way.

9. Put work aside without committing it#

Sometimes you don’t want to undo anything, just get a clean folder for a minute: to pull, to switch branches, or to check whether a bug was there before your changes.

Shell
git stash -u        # put all changes aside, including new files
# ...do something else...
git stash pop       # bring them back

-u includes untracked (new) files, which plain git stash leaves behind. git stash list shows everything you’ve stashed.

Which command do I need?#

What happened Command
Messed up a file, not committed git restore <file>
Staged a file by mistake git restore --staged <file>
Typo in the last commit message, not pushed git commit --amend
Last commit was too early, not pushed git reset --soft HEAD~1
Want the last commit gone entirely, not pushed git reset --hard HEAD~1
Bad commit already pushed git revert <commit>
Committed to the wrong branch git branch <new> then git reset --hard HEAD~1
Lost a commit or branch after a reset git reflog, then git reset --hard HEAD@{n}
Need a clean folder for a minute git stash -u, later git stash pop

The pattern behind all of it: restore works on files, reset moves your branch (safe only for commits nobody else has), revert adds an opposite commit (safe everywhere), and reflog is the safety net under all of them.

If your team’s repository, deployments or release process could use a tidy-up, I help with that as part of API and integration work.

One email when a new post is published. Unsubscribe any time.

Comments