Version Control

Undoing Mistakes in Git Safely

How to discard edits, recover a deleted file, revert a bad commit and rewrite local history in Git, with guidance on what is safe once work is shared.

On this page
  1. First, work out where the mistake is
  2. Discarding uncommitted edits
  3. Recovering a deleted file
  4. Fixing your latest commit
  5. Backing out a commit on a shared branch: revert
  6. Rewriting local history: reset
  7. Tidying local commits before sharing
  8. When you think you have lost everything
  9. Habits that make undoing easier

First, work out where the mistake is

Git has several ways to undo things, and choosing the right one depends on one question: how far has the mistake travelled?

  1. Uncommitted edits in your working files.
  2. Staged changes that you have not committed.
  3. A local commit that you have not shared.
  4. A pushed commit that others may have already pulled.

The first three affect only you, so you have plenty of freedom. The fourth involves other people’s copies, and the safe tools are different. Always run git status and git log --oneline first to see where you stand before running any undo command.

A caution that applies throughout: some commands discard work permanently. If an edit was never committed, Git has no record of it, and a discard cannot be undone. When unsure, make a backup copy of the folder or commit to a temporary branch before experimenting.

Discarding uncommitted edits

To throw away your changes to a file and return it to its last committed state:

git restore notes.txt

This is destructive: the edits are gone. Check with git diff first if you are not certain you want to lose them.

To unstage something you added by mistake, keeping the edits in your working copy:

git restore --staged notes.txt

If you want to set work aside temporarily rather than discard it, use the stash:

git stash
git stash pop

The first command saves your uncommitted changes and cleans the working directory; the second brings them back.

Recovering a deleted file

If you deleted a file that was committed earlier, it is still in the history. Restore it from the last commit:

git restore deleted-file.txt

If the deletion has been committed too, restore it from an earlier commit by naming that commit:

git restore --source=<commit> -- deleted-file.txt

You can find the commit by searching the history, for example with git log -- deleted-file.txt, which lists the commits that touched that path. Files that were never committed cannot be recovered this way, which is one more reason to commit often.

Fixing your latest commit

If you have just committed and spot a typo in the message or forgot a file, and you have not shared the commit, amend it:

git add forgotten-file.txt
git commit --amend

This replaces the latest commit with a corrected one. Because it produces a new commit with a new identity, it counts as rewriting history, so only do it on commits nobody else has.

Backing out a commit on a shared branch: revert

When a bad commit has already been pushed, the safe approach is to add a new commit that reverses it:

git revert <commit>

Git creates a commit applying the opposite changes. The original commit stays in history, along with the correction, so everyone’s copies remain consistent and the record is honest about what happened. Write a message explaining why the revert was needed. Reverting a merge commit needs extra care and an additional option to say which side to keep, so look it up in the Git documentation before attempting it.

Rewriting local history: reset

git reset moves the current branch to a different commit. It comes in three common flavours:

  • git reset --soft <commit> moves the branch but keeps all changes staged.
  • git reset --mixed <commit> (the default) moves the branch and unstages changes, keeping them in your files.
  • git reset --hard <commit> moves the branch and discards changes to tracked files in your working directory.

For example, to remove your last local commit but keep the work for another go:

git reset --soft HEAD~1

Here HEAD~1 means the commit before the current one. The --hard form is the dangerous one, since it throws away uncommitted work. Use it only when you are sure.

Never use reset to remove commits that have been pushed to a shared branch. Your copy and everyone else’s would then disagree about history, and the usual result is confusion, duplicated commits and sometimes lost work. If you must rewrite your own branch that you pushed, tell your colleagues first, and prefer the safer force option git push --force-with-lease, which refuses to overwrite commits you have not seen. Many teams forbid force-pushing to shared branches altogether.

Tidying local commits before sharing

An interactive rebase lets you reorder, combine or reword commits on your own branch before others see it:

git rebase -i HEAD~3

This opens a list of the last three commits and lets you choose what to do with each. It is a useful way to turn messy work-in-progress commits into a clean sequence ahead of a pull request. The same rule applies: do it only on commits that are yours alone.

When you think you have lost everything

Git keeps a private log of where your branch tips and HEAD have been, called the reflog:

git reflog

If you reset too far or deleted a branch, the lost commits often still appear there for some time. Find the entry from before the mistake and create a branch at it:

git branch rescue <commit>

Git does eventually clean up unreachable commits, so act soon rather than later. The reflog also only covers your own copy of the repository, not other people’s.

Habits that make undoing easier

  • Commit early and in small pieces, so there are many safe points to return to.
  • Check git status and git diff before destructive commands.
  • Work on a branch rather than directly on the main line.
  • Prefer revert over reset once something is shared.
  • If you are stuck partway through a merge, you can often back out with git merge --abort; the guide to merge conflicts describes this.
  • When you are not sure, copy the folder first. Disk space is cheaper than lost work.

Command behaviour can vary between Git versions, so confirm the details with the built-in help, such as git help revert, and follow your team’s rules about shared branches.

The complete guide to version control

Version Control Explained for Engineers
Read the guide

Keep exploring

Guides from other topics

All topics →