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
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 checkoutis like a time machine for your working directory and staging area. It lets you switch branches or restore files from a specific commit.git resetis 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 checkoutcan move changes left (from staging to working) or right (from history to working/staging).git resetmoves 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:
--hardis 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 ingit reflogand 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 --hardwhen you meant--mixed— destroys uncommitted work you wanted to keep. - Forgetting the
--ingit checkout -- file, causing Git to interpretfileas a branch name. - Unstaging with
git reset HEAD fileand then expecting the file to be deleted — it only moves it back to the working directory. - Assuming
git resetremoves the changes permanently — it doesn't; the reflog can recover lost commits. - Using
git checkoutto revert committed changes when you actually needgit resetorgit revert.
Variations
- Use
git restoreas a modern alternative togit checkout -- <file>andgit restore --stagedinstead ofgit reset HEAD <file>. - Use
git revertto undo a commit in a shared history without rewriting history, creating a new reverse commit. - Use
git reset --softas 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~1to 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~1after recovering data.
Key takeaways
git checkoutis for restoring files or switching branches; it's less destructive thanreset.git resetmoves 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 ingit checkoutto avoid ambiguity. - The reflog is your safety net — even after a hard reset, you can recover lost commits within 90 days.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.