Core idea: fix mistakes without losing work
In small projects, most Git “oops” moments fall into two categories: (1) you changed the wrong thing (staged the wrong file, edited the wrong lines, deleted something), or (2) you recorded the wrong history (bad commit message, forgot a file, committed too early). Git gives you tools that are safe by default when you choose the right command for the situation. The key is to decide what you want to change: the staging area, the working tree, or the commit history.
Quick decision rules (use these first)
- Undo what is staged (but keep your edits):
git restore --staged - Discard local edits in a file (restore from last commit):
git restore - Bring back a deleted file:
git restore(orgit checkout --in older Git) - Undo a commit safely on a shared branch:
git revert - Rewrite local history (not shared):
git reset --soft/--mixed/--hard - Fix the last commit (message or content) before pushing:
git commit --amend - Recover “lost” commits after a reset:
git reflog
Scenario 1: You staged the wrong file (unstage without losing edits)
Problem: you ran git add and now the staging area includes changes you don’t want in the next commit. You want to keep the edits in your working tree, just remove them from the staging area.
Step-by-step: unstage a specific file
git statusgit restore --staged path/to/filegit statusAfter this, the file’s changes remain in your working directory, but they are no longer staged.
Unstage everything
git restore --staged .Use this when you want to rebuild the staging area carefully (for example, staging only certain files or hunks).
Scenario 2: You want to discard local edits safely (restore from last commit)
Problem: you edited a file and want to throw away those local modifications and return the file to the last committed state.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Step-by-step: discard edits in one file
git statusgit restore path/to/filegit statusThis replaces the working-tree version of the file with the version from HEAD (your last commit on the current branch). It does not affect other files.
Discard edits in multiple files
git restore .Be careful: this discards all unstaged changes in the current directory tree. If you might want the work later, consider stashing or committing to a temporary branch (not covered here) before restoring.
Scenario 3: You deleted a file and want it back
Problem: you removed a tracked file (accidentally or intentionally) and now you want to restore it.
Case A: file deleted in working tree, not committed
git statusgit restore path/to/deleted-fileThis restores the file from HEAD.
Case B: file deletion is staged (you ran git rm or staged the deletion)
If the deletion is staged, first unstage it, then restore the file content:
git restore --staged path/to/deleted-filegit restore path/to/deleted-fileThink of it as: first fix the staging area, then fix the working tree.
Scenario 4: You need to undo a commit: revert vs reset
Problem: a commit is wrong and you want to undo its effect. There are two main approaches, and choosing the right one prevents headaches.
Option 1 (safe for shared history): git revert
git revert creates a new commit that applies the inverse of an earlier commit. This keeps history linear and is the default choice when the commit may already be on a branch others pull from (or anything you already pushed and want to avoid rewriting).
Step-by-step: revert a commit
git log --onelinegit revert <commit-sha>git log --onelineResult: history shows both the original commit and the revert commit. The project content reflects the undo.
Option 2 (rewrite local history): git reset
git reset moves the current branch pointer (and optionally changes the staging area and working tree). This rewrites history, so it is appropriate when the commits are local (not pushed) or when you are certain rewriting is acceptable.
Understand the three reset modes
git reset --soft <target>: movesHEADto<target>, keeps changes staged. Use when you want to “uncommit” but keep everything ready to recommit (e.g., split into multiple commits).git reset --mixed <target>(default): movesHEAD, unstages changes, keeps edits in working tree. Use when you want to redo staging/commit selection.git reset --hard <target>: movesHEADand resets staging + working tree to match. Use only when you truly want to discard local changes (and you have no uncommitted work you care about).
Practical targets: “go back one commit”
To move back one commit from the current HEAD:
git reset --soft HEAD~1git reset --mixed HEAD~1git reset --hard HEAD~1Pick exactly one based on whether you want changes staged, unstaged, or discarded.
Rules of thumb: when each is appropriate
- Use
git revertwhen the commit is already shared (pushed) or you want an audit-friendly “undo” in history. - Use
git resetwhen you are cleaning up local commits before sharing, or when you want to reorganize commits (message/content) without leaving “revert noise.” - Avoid
--hardunless you are sure you do not need the current working-tree changes. If unsure, use--mixedfirst, inspect withgit status, then decide.
Scenario 5: Fix the last commit with git commit --amend
Problem: your last commit is almost right, but you need to fix the message, add a forgotten file, or adjust a small change.
Amend the message only
git commit --amendYour editor opens with the previous message; edit it and save. This creates a new commit object that replaces the previous last commit.
Amend content (add forgotten changes)
1) Make the additional edits or add the missing file. 2) Stage them. 3) Amend.
git add path/to/forgotten-filegit commit --amendImportant: amending rewrites the last commit. If you already pushed that commit, amending will diverge from the remote history. In small projects, a safe habit is: amend freely before pushing; after pushing, prefer git revert or create a new commit.
Safe recovery: use git reflog to find “lost” commits
Problem: you used git reset (or amended) and now a commit seems gone. Often it is not truly lost: Git records where HEAD and branch tips have been in the reflog. You can use it to locate the previous commit SHA and restore it.
Step-by-step: find prior states
git reflogYou will see entries like HEAD@{0}, HEAD@{1}, each with a commit SHA and an action (reset, commit, amend).
Restore by resetting back to a reflog entry
If you want to return your branch to a previous state:
git reset --hard HEAD@{1}Or use the SHA shown in the reflog:
git reset --hard <sha-from-reflog>If you are unsure about discarding current work, prefer --mixed first, inspect with git status, then decide whether to go --hard.
Restore a lost commit without moving your current branch (create a rescue branch)
A cautious approach is to create a new branch pointing at the lost commit:
git branch rescue <sha-from-reflog>This preserves access to the commit while you decide how to integrate it.
Exercises: intentional mistakes, two fixes, compare history
Exercise 1: Stage the wrong file, then fix it two ways
Goal: learn to correct staging mistakes without losing edits.
- Create two files and edit both.
- Accidentally stage both, but you only want one staged.
# Create files (example names; use any files you like) git create-file notes.txt git create-file todo.txt# Edit both files (use your editor) # Then stage everything by mistake git add . git statusMethod A (recommended): unstage the unwanted file only.
git restore --staged todo.txt git statusMethod B (broader): unstage everything, then stage only what you want.
git restore --staged . git add notes.txt git statusCompare: both methods keep your edits. Method A is faster when only one file is wrong; Method B is useful when you want to rebuild the staging set from scratch.
Exercise 2: Discard a bad edit vs recover a deleted file
Goal: practice restoring content from HEAD.
- Make a bad edit in a tracked file and discard it.
- Delete a tracked file and restore it.
# Bad edit (in a tracked file) # Edit README.md and save unwanted changes git status git restore README.md git status# Delete and restore rm config.json git status git restore config.json git statusVariation: stage the deletion first, then recover using the two-step unstage + restore.
rm config.json git add config.json git status git restore --staged config.json git restore config.json git statusExercise 3: Undo a commit two ways and compare history
Goal: see the difference between “undo by new commit” (revert) and “rewrite local history” (reset).
Setup: create a commit that introduces an obvious mistake (e.g., add a line “DEBUG=true” to a config file).
# Make a mistake and commit it echo "DEBUG=true" >> app.conf git add app.conf git commit -m "Enable debug" git log --oneline --decorate -5Method A: revert (keeps history, adds an undo commit).
git revert HEAD git log --oneline --decorate -5Observe: you now have two commits: the mistake and the revert. The file content should have the debug line removed.
Method B: reset (rewrite history). To try this, first recreate the mistake commit again (or do this exercise in a fresh branch). Then reset back one commit using different modes and observe git status.
# Recreate mistake commit (if needed) echo "DEBUG=true" >> app.conf git add app.conf git commit -m "Enable debug (again)"# Option 1: keep changes staged git reset --soft HEAD~1 git status# Option 2: keep changes but unstaged git reset --mixed HEAD~1 git status# Option 3: discard changes completely (only if you truly want to) git reset --hard HEAD~1 git statusCompare history: revert adds a new commit and preserves the original commit in the log; reset removes the commit from the branch history (but it is typically recoverable via reflog).
Exercise 4: Amend the last commit, then recover with reflog
Goal: practice rewriting the last commit and recovering prior versions.
1) Make a commit with a bad message. 2) Amend it. 3) Use reflog to find the pre-amend commit.
# Make a commit with a bad message echo "typo" >> notes.txt git add notes.txt git commit -m "WIP" git log --oneline -3# Amend message (and optionally add more changes) git commit --amend# Inspect reflog to find the old commit git reflogRestore the old commit into a rescue branch so you can compare:
git branch pre-amend <sha-from-reflog> git log --oneline --decorate -5Observe: the amended commit has a different SHA than the original. The reflog and rescue branch demonstrate the “recoverable by default” habit: even after rewriting, you can usually get back to earlier states.