Working Code
Example 1: Discarding file changes with git restore
When you accidentally break a file:
git init
echo "Original content" > file.txt
git add file.txt
git commit -m "feat: add file"
# Accidentally overwrite an important file
echo "Wrong content overwrites everything" > file.txt
cat file.txt
Output:
Wrong content overwrites everything
# Restore to the last committed state
git restore file.txt
cat file.txt
Output:
Original content
git restore discards working directory changes. This cannot be undone.
Example 2: Unstaging
echo "Modified content" > file.txt
git add file.txt
git status
Output:
Changes to be committed:
modified: file.txt
# Unstage only (keep working directory changes)
git restore --staged file.txt
git status
Output:
Changes not staged for commit:
modified: file.txt
The file content is preserved; only the staging is undone.
Example 3: Reverting a specific commit with git revert
git init
echo "v1" > version.txt && git add version.txt && git commit -m "feat: v1"
echo "v2" >> version.txt && git add version.txt && git commit -m "feat: v2"
echo "v3" >> version.txt && git add version.txt && git commit -m "feat: v3"
git log --oneline
Output:
c3d4e5f feat: v3
b2c3d4e feat: v2
a1b2c3d feat: v1
Undo the v2 commit's changes (as a new commit):
git revert b2c3d4e
Save and close the editor. Output:
[main d5e6f7g] Revert "feat: v2"
1 file changed, 1 deletion(-)
cat version.txt
Output:
v1
v3
The v2 changes are undone, but history is preserved.
Try It Yourself
git reset: three ways to undo commits
git init
echo "a" > file.txt && git add file.txt && git commit -m "Commit A"
echo "b" >> file.txt && git add file.txt && git commit -m "Commit B"
echo "c" >> file.txt && git add file.txt && git commit -m "Commit C"
git log --oneline
Output:
c3d4e5f Commit C
b2c3d4e Commit B
a1b2c3d Commit A
--soft: Undo the commit, keep changes staged
git reset --soft HEAD~1
git log --oneline
Output:
b2c3d4e Commit B
a1b2c3d Commit A
git status
Output:
Changes to be committed:
modified: file.txt <- Commit C's changes remain staged
Useful when you just want to rewrite the commit message.
--mixed: Undo commit + unstage, keep files (default)
git reset HEAD~1 # --mixed is the default
git log --oneline
git status
Output:
a1b2c3d Commit A
Changes not staged for commit:
modified: file.txt <- Changes remain in working directory
Use this when you want to re-stage files differently.
--hard: Undo commit + unstage + discard file changes
git reset --hard HEAD~1
git log --oneline
cat file.txt
Output:
a1b2c3d Commit A
a
Commits B and C are completely gone. Use this with extreme caution.
git reset comparison
| Command | Commit | Staging | Working Directory |
| --------- | ------ | ------- | ----------------- |
| --soft | Undone | Kept | Kept |
| --mixed | Undone | Undone | Kept |
| --hard | Undone | Undone | Undone |
git reflog: your safety net
When you lose commits via git reset --hard or other mistakes:
# View the log of all HEAD movements
git reflog
Output:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
c3d4e5f HEAD@{1}: commit: Commit C
b2c3d4e HEAD@{2}: commit: Commit B
a1b2c3d HEAD@{3}: commit: Commit A
Recover a lost commit:
git reset --hard c3d4e5f # or HEAD@{1}
git log --oneline
Output:
c3d4e5f Commit C <- Recovered!
b2c3d4e Commit B
a1b2c3d Commit A
reflog is Git's local record of every HEAD movement. Even commits "deleted" by git reset --hard can be recovered.
"Why?" -- When to use each command
| Scenario | Correct Command |
| ------------------------------------ | ------------------------------- |
| Discard file changes (before commit) | git restore filename |
| Unstage a file | git restore --staged filename |
| Fix the last commit message | git commit --amend |
| Undo unpushed commits | git reset |
| Undo pushed commits | git revert |
| Recover accidentally lost commits | git reflog |
The key difference: reset vs revert
reset: A -> B -> C -> (D removed)
^
HEAD
revert: A -> B -> C -> D -> D' (inverse of D)
^
HEAD
Always use revert for pushed code. Using reset to delete pushed commits causes history conflicts with teammates.
Common Mistakes
Mistake 1: Using --hard recklessly
# Warning! Cannot be undone easily
git reset --hard HEAD~3 # All changes from 3 commits are gone
# Check what you'll lose first
git diff HEAD~3 HEAD # See what will be removed
git reset --hard HEAD~3 # Then decide
Mistake 2: Using reset on pushed commits
git push origin main # Already pushed
git reset --hard HEAD~1 # Delete commit locally
git push origin main # Error!
# error: failed to push some refs
# Force push (causes problems for teammates)
git push --force # Never do this on shared branches
Mistake 3: Conflict during revert
git revert b2c3d4e # Conflict may occur
# CONFLICT: ...
# Resolve the conflict, then
git add filename
git revert --continue
Deep Dive
git restore vs git checkout (legacy approach)
Before Git 2.23, checkout was used to restore files:
# Legacy approach
git checkout -- file.txt # Restore working directory
git checkout HEAD -- file.txt # Restore from a specific commit
# Modern approach (clearer intent)
git restore file.txt # Restore working directory
git restore --staged file.txt # Unstage
git restore --source=HEAD~2 file.txt # Restore from 2 commits ago
Restoring a specific file from an older commit
# Restore a file from 5 commits ago
git restore --source=HEAD~5 important-file.txt
# Restore from a specific commit hash
git restore --source=a1b2c3d config.yaml
# The restored file is in your working directory; review and commit
git diff config.yaml
git add config.yaml
git commit -m "revert: restore config.yaml to previous version"
git bisect: finding the bug-introducing commit
When you don't know which commit introduced a bug:
git bisect start
git bisect bad HEAD # Current state: has the bug
git bisect good a1b2c3d # This commit was working fine
# Git checks out a middle commit
# Test and report the result
git bisect good # This commit is fine
git bisect bad # This commit has the bug
# After a few rounds, Git identifies the culprit
# When done, return to normal
git bisect reset
- Modify a file and restore it with
git restore. - Stage a file and unstage it with
git restore --staged. - Create 3 commits and test
git reset --soft HEAD~1,git reset --mixed HEAD~1, andgit reset --hard HEAD~1separately. Compare the differences. - Delete a commit with
git reset --hard HEAD~1, then find and recover it withgit reflog. - Undo a commit with
git revertand check the history withgit log --oneline.
Q1. How do you safely undo a commit that has already been pushed and shared with teammates?
- A)
git reset --hard HEAD~1thengit push --force - B)
git revert commit-hash - C)
git reset --soft HEAD~1thengit push - D)
git branch -Dand recreate it