When you work with Git, a wrong command can feel scary. In this post you will learn three safe ways to undo changes, why each method matters, and how to bring your repository back to a healthy state without losing work.

Why Undoing Matters

Every developer makes a typo, a bad merge, or pushes a commit they later regret. Knowing the right tool prevents panic and keeps the project history readable. It also saves you time: you won’t need to ask teammates to redo work or create a messy "fix" commit.

The Three Core Undo Techniques

Git provides several commands that look similar but behave very differently. The three you will use most often are:

  1. git revert – adds a new commit that undoes a previous one.
  2. git reset – moves the branch pointer backward, optionally changing the working files.
  3. git checkout (or the newer git switch) – restores a file or a whole tree to a previous state.

Below we explore each one with real examples.

Using git revert – Safe for Shared History

git revert is the friendliest way to undo a commit that is already public. It creates a new commit that applies the opposite changes of the target commit. The original commit stays in history, so collaborators do not see a sudden jump.

# Suppose you want to undo commit abc123
git revert abc123

Line by line:

  • git revert tells Git you want a new commit that reverses something.
  • abc123 is the short hash of the commit you want to undo. You can find it with git log.
  • Git opens your editor for a commit message. It pre‑fills a message like "Revert "Add login page"". Save and close to finish.

After the command, git log shows a new commit on top. The code base now reflects the state before abc123, but the history still records that the bad commit existed. This is ideal when the commit is already pushed to a shared remote.

When Not to Use git revert

  • If you need to erase a secret key that was accidentally committed, a revert will still expose the key in history. Use git reset or filter‑branch in that case.
  • If you want to clean up a series of small, local commits before sharing, git reset is cleaner.

Using git reset – Rewriting Local History

git reset moves the current branch pointer to a previous commit. It has three modes that control what happens to your working directory and staging area (the index).

# Move back two commits, keep changes in working files
git reset --soft HEAD~2

# Move back two commits, keep changes staged
git reset --mixed HEAD~2   # default mode

# Move back two commits, discard all changes
git reset --hard HEAD~2

Explanation:

  • HEAD~2 means “the commit two steps before the current one”.
  • --soft moves the branch pointer but leaves all changes staged (ready to commit again).
  • --mixed (the default) moves the pointer and unstages the changes, leaving them in your working files.
  • --hard throws away the changes completely, resetting both index and working files.

Practical Scenario

You accidentally committed a debug log file and pushed it. You realize the commit should never have been made, and nobody has pulled it yet. You can:

  1. git reset --soft HEAD~1 – the bad commit disappears, but the file is still staged.
  2. git rm debug.log – remove the file.
  3. git commit -m "Remove debug log" – create a clean commit.
  4. git push --force-with-lease – rewrite the remote branch safely.

Warning: --hard deletes work permanently. Use it only when you are sure the changes are not needed.

Using git checkout / git switch – Restoring Files

Sometimes you only need to revert a single file, not the whole commit history. git checkout (or the newer git switch --discard-changes) can bring a file back to the version in a specific commit.

# Restore a file to the state in the last commit
git checkout -- src/app.js

# Restore a file to a specific commit
git checkout abc123 -- src/app.js

What happens:

  • The -- tells Git that everything after it is a file path, not a branch name.
  • The file in your working directory is replaced with the version from the given commit.
  • The change is not staged automatically; you need to git add src/app.js if you want to commit it.

Quick Tip

If you only want to see what a file looked like before, use git show:

git show abc123:src/app.js

It prints the file content to the terminal without altering anything on disk.

Finding Lost Commits with git reflog

Even with careful use of reset and revert, you might lose a commit you thought was gone. Git keeps a reflog – a log of where HEAD (the current branch tip) has pointed over time. It records moves caused by reset, rebase, checkout, and more.

# Show recent HEAD movements
git reflog

You’ll see lines like:

1a2b3c4 HEAD@{0}: reset: moving to HEAD~2
5d6e7f8 HEAD@{1}: commit: Add feature X
9a0b1c2 HEAD@{2}: commit: Fix typo

If you realize you need the commit at HEAD@{2}, you can restore it:

# Create a new branch at that commit
git branch rescue 9a0b1c2
# Or reset current branch back to it
git reset --hard 9a0b1c2

The reflog is local only; it disappears after a few weeks of inactivity, but it’s a lifesaver for accidental resets.

A Small Checklist for Safe Undoing

  • Public commit? Use git revert.
  • Local commit you haven’t pushed? Use git reset with the appropriate mode.
  • Only a file went wrong? Use git checkout (or git restore in newer Git).
  • Can’t find a lost commit? Look at git reflog.
  • Never push a forced rewrite without --force-with-lease – it protects teammates.

FAQ

How do I undo a merge commit?

You can revert a merge with git revert -m 1 <merge‑hash>. The -m 1 tells Git which parent should be kept. If the merge isn’t pushed yet, you can also git reset --hard HEAD~1.

Is git reset --hard ever safe to use?

It is safe when you are absolutely sure the changes you are discarding are not needed and are only in your local copy. After a hard reset, the changes disappear from the working directory and the index.

What’s the difference between git checkout and git restore?

git restore was introduced to separate the responsibilities of checkout. git restore focuses on files, while checkout still handles branch switching. Both work the same for restoring a file, but git restore has clearer options.

Wrapping up

Undoing in Git is not magic; it’s a set of well‑defined commands that let you move the branch pointer, add opposite commits, or fetch old file versions. Choose the tool that matches the scope of your mistake and whether the commit is already shared. With revert, reset, checkout, and reflog in your toolbox, you’ll spend less time panicking and more time coding.