Undo Changes with Checkout and Reset

Learn to undo changes with checkout and reset in this Git Tutorial. Step-by-step instructions, comparisons, and troubleshooting to master Git undo operations.

Focus: undo changes with checkout and reset

Sponsored

You've been working on a feature branch, making commits, and now you realize it's all wrong. Or you've accidentally staged a file you didn't mean to, and you need to unstage it before commiting. Or maybe you just want to discard a change before it becomes permanent. Git offers several ways to undo work, but the two most fundamental commands are git checkout and git reset. In this lesson, you'll learn exactly what each does, when to use them, and how to escape your own mistakes without panic.

The problem this lesson solves

You've made a commit and then realized it was a mistake, or you've modified a file and want to revert it to the last committed state. Undoing changes is one of the most common and most feared operations in Git because a wrong reset can feel like you've lost work forever. The pain point is real: you have multiple states — working directory, staging area, and commit history — and you need to know which command affects which state. Without that clarity, you risk losing work or creating a mess that confuses your collaborators.

Core concept / mental model

Think of Git as a three-tiered snapshot system: your working directory (the files you're editing), the staging area (where you selectively place changes for the next commit), and the commit history (a series of snapshots).

  • git checkout is like a time machine for your working directory and staging area. It lets you switch branches or restore files from a specific commit.
  • git reset is a more powerful tool that moves the branch pointer (HEAD) and optionally resets the staging area and working directory to a previous commit.

A common analogy: you're writing a book. checkout lets you go back to an earlier draft of a chapter, while reset aggressively rewrites the story by making a previous draft the new current version, possibly discarding pages you've written since.

Visualize the flow:

Working Directory <--> Staging Area <--> Commit History (HEAD)
  • git checkout can move changes left (from staging to working) or right (from history to working/staging).
  • git reset moves the HEAD pointer backward and can cascade changes right-to-left depending on the mode.

How it works step by step

Undoing changes in your working directory

If you've modified a file but haven't staged it, you can revert that file to the last committed state using git checkout. Here's the pattern:

git checkout -- <file>

The -- tells Git you're referring to a file path, not a branch name. This command discards all local modifications to that file — it's destructive, so use it carefully.

Unstaging a file

If you've staged a file with git add but want to remove it from the staging area, git reset can help:

git reset HEAD <file>

This unstages the file but leaves your working directory changes intact. Think of it as "move the file back to the working directory" — the content stays, but it's no longer marked for the next commit.

Reverting a commit

If you've made a commit and need to undo it, you have two main options. git reset removes the commit from history (as if it never happened), while git revert creates a new commit that reverses the changes. We'll focus on reset here.

The three modes of reset:

  • --soft: Moves HEAD but leaves the staging area and working directory unchanged. Useful for undoing a commit while keeping all changes staged.
  • --mixed (default): Unstages the changes but leaves the working directory intact. Good for undoing a commit while keeping your edits.
  • --hard: Discards everything — HEAD, staging, and working directory reset to the specified commit. Dangerous — you lose all uncommitted changes.

What about checkout for commits?

git checkout is primarily for switching branches, but you can also use it to view a previous commit in a "detached HEAD" state. That's a useful way to inspect history without affecting your current branch. We'll cover more in the next lesson.

Hands-on walkthrough

Let's practice in a fresh repository. Open your terminal and follow along.

Setup

mkdir undo-lab
cd undo-lab
git init
echo "Hello Git" > app.txt
git add app.txt
git commit -m "Initial commit"

Now let's make a mistake. Edit app.txt to add a second line:

echo "This is a change" >> app.txt

Check the status:

git status
# Output:
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git restore <file>..." to discard changes in working directory)
#   modified:   app.txt

Undo this uncommitted change with checkout:

git checkout -- app.txt
cat app.txt
# Output:
# Hello Git

The change is gone — the file is back to the last committed state.

Unstaging a file

Now let's stage a change and then unstage it:

echo "Another change" >> app.txt
git add app.txt
git status
# On branch master
# Changes to be committed:
#   (use "git restore --staged <file>..." to unstage)
#   modified:   app.txt

Unstage it with reset:

git reset HEAD app.txt
# Output:
# Unstaged changes after reset:
# M app.txt

Now git status shows the change as unstaged again, but the file itself still has the modification.

Reset with --soft and --hard

Let's create another commit and then undo it. Add a file and commit:

echo "New file" > new.txt
git add new.txt
git commit -m "Add new.txt"

Now we decide this commit was a mistake. First, let's see what happens with --soft:

git reset --soft HEAD~1
# HEAD is now at <initial commit hash> login

Check the status: new.txt is staged, ready to be committed again. We can undo this by resetting again, this time with --mixed:

git reset HEAD~1  # this is the same as --mixed

Now new.txt is untracked, and no trace of the second commit remains in history. If we instead want to completely wipe out the commit and all associated uncommitted changes, we'd use --hard:

git reset --hard HEAD~1

Pro tip: --hard is irreversible, but Git keeps a reference called the reflog for up to 90 days. If you accidentally hard reset, you can recover by finding the old commit hash in git reflog and resetting back to it. Don't panic — there's usually a way back.

Compare options / when to choose what

Scenario Command Effect Risk
Discard uncommitted changes in a file git checkout -- <file> Restores file to last commit Medium — loses working changes
Unstage a file (but keep changes) git reset HEAD <file> Moves file from staging to working Low
Undo last commit, keep changes staged git reset --soft HEAD~1 HEAD moves back, changes stay in staging Low
Undo last commit, keep changes in working dir git reset --mixed HEAD~1 HEAD moves back, changes unstaged Low
Undo last commit and discard all changes git reset --hard HEAD~1 Everything goes back High — data loss possible

Choosing the right command depends on where your mistake exists and what you want to keep. If you want to keep your edits, avoid --hard. If you want to completely erase a commit and its changes, --hard is the tool.

Troubleshooting & edge cases

"I accidentally ran git reset --hard and lost everything!"

Recovery is possible via the reflog. Run git reflog to see your actions, then find the commit hash before the reset and use git reset --hard <hash> to restore. This works for up to 90 days by default.

"I unstaged a file but accidentally deleted it from working directory"

If you unstaged and then deleted the file, you can recover it from the index or HEAD using git checkout HEAD -- <file> or git restore <file>. The file is still in Git's object database.

"The git checkout -- file syntax never works for me"

Make sure you use -- between checkout and the file path. Without it, Git might interpret the argument as a branch name. For newer Git versions, git restore is a clearer alternative.

What you learned & what's next

You now know how to undo changes with checkout and reset — two powerful tools in your Git toolbox. You can discard uncommitted changes, unstage files, and even undo entire commits while preserving or discarding your work as needed. You also learned that reset has three modes (--soft, --mixed, --hard) that control how far back the changes cascade.

Next up in your Git Tutorial, you'll dive into more advanced undo techniques like git revert for safely undoing commits that have already been shared with others. You'll also explore the reflog in depth for those "oops" moments.

Now that you can handle mistakes with confidence, you're ready to collaborate more boldly — because you know you can always roll back. Keep practicing, and soon undoing changes will feel as natural as making them.

Practice recap

Practice by creating a test repository and simulating each undo scenario: modify and discard a file, stage and unstage, create a commit and undo it with all three reset modes. Use git reflog to inspect your history and recover from a hard reset — this builds muscle memory for real-world mistakes.

Common mistakes

  • Using git reset --hard when you meant --mixed — destroys uncommitted work you wanted to keep.
  • Forgetting the -- in git checkout -- file, causing Git to interpret file as a branch name.
  • Unstaging with git reset HEAD file and then expecting the file to be deleted — it only moves it back to the working directory.
  • Assuming git reset removes the changes permanently — it doesn't; the reflog can recover lost commits.
  • Using git checkout to revert committed changes when you actually need git reset or git revert.

Variations

  1. Use git restore as a modern alternative to git checkout -- <file> and git restore --staged instead of git reset HEAD <file>.
  2. Use git revert to undo a commit in a shared history without rewriting history, creating a new reverse commit.
  3. Use git reset --soft as a lightweight way to undo a commit while keeping all changes staged for a new commit.

Real-world use cases

  • A developer accidentally stages a config file with secrets and needs to unstage it before committing — git reset HEAD config.file.
  • You realize the last commit on your feature branch has a typo in a critical file; you use git reset --soft HEAD~1 to amend it without losing work.
  • A junior dev pushes a broken commit to a fork and wants to remove it from local history before rebasing — git reset --hard HEAD~1 after recovering data.

Key takeaways

  • git checkout is for restoring files or switching branches; it's less destructive than reset.
  • git reset moves the branch pointer and can unstaged, uncommit, or discard working changes depending on the mode.
  • The three reset modes (--soft, --mixed, --hard) give you precise control over what's saved.
  • Always use -- before file paths in git checkout to avoid ambiguity.
  • The reflog is your safety net — even after a hard reset, you can recover lost commits within 90 days.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.