Undo & Recovery
7 pages · ~5 min✓ Reviewed
Builds on Rebase vs Merge. Next up: Stash & Worktrees.
Undo Mental Model: Three Areas
1. Three Areas
- Working treefiles on disk
- Indexstaging,
git add - RepositoryHEAD commit,
git commit
- Every undo command targets one or more of these areas
- Pick the area first, then pick the command
2. Rewrite or Append
| History | Undo by |
|---|---|
| Local, unpublished | Rewrite it: reset, amend |
| Pushed, shared | Add new commits only: revert |
3. Check Before Undoing
git statusshows which area holds the changegit log --oneline -5shows the latest commits- Run both before any undo
4. What Can Be Lost
| State | Recovery |
|---|---|
| Committed | Reflog ~90 days; unreachable ~30 days, until gc |
| Never committed | None |
git restore and reset --hard on never-committed edits cannot be undone.
Restore and Unstage Files
1. What restore targets
| Command | Effect |
|---|---|
git restore <file> | worktree ← index; edits gone |
git restore --staged <file> | unstage; edits kept |
git restore --source=<c> <file> | file from any commit → worktree |
--staged --worktree | updates index and worktree |
Discarded uncommitted edits are unrecoverable: never committed, so not in the reflog.
2. Source and hunks
--sourcetakes any commit:HEAD~2, a branch, a tag-pasks per hunk: y discard, n keep, s split- Other hunks in the file stay untouched
git restore --source=HEAD~2 app.py git restore --source=HEAD~2 --staged --worktree app.py git restore -p app.py
old content into worktree; into both; chosen hunks only
3. Older equivalents
| Older | Modern |
|---|---|
git checkout -- <file> | git restore <file> |
git reset HEAD <file> | git restore --staged <file> |
git checkout <c> -- <file> | git restore --source=<c> <file> |
Reset: Soft, Mixed, Hard
1. What reset moves
git reset <commit>moves the current branch pointer to that commit- The mode decides what else is rewound: index, working tree
| Mode | Moves | Your changes |
|---|---|---|
--soft | HEAD | staged |
--mixed (default) | HEAD + index | unstaged, still on disk |
--hard | HEAD + index + worktree | lost |
2. Undo recent commits
--soft HEAD~1undoes the last commit, files stay staged- Ideal for redoing or squashing the last commits
HEAD~3goes back three commits
git reset --soft HEAD~1 # keep staged git reset HEAD~3 # mixed: unstaged
soft keeps the index; mixed drops it
3. Hard reset and the way out
--harddiscards uncommitted edits, so run git status firstORIG_HEADrecords the previous tip, so it undoes a bad merge or reset
git reset --hard ORIG_HEADback to the tip before the merge or reset
reset --hard on edits that were never committed cannot be undone.
Amend and Revert
1. Amend the last commit
git commit --amendreplaces the last commit- Fix its message, or stage more files and add them without a new commit
--no-editkeeps the message and adds the newly staged changes
git add forgotten.txt
git commit --amend --no-editadds the file, same message
Amend creates a new commit hash. Never amend a commit that is already pushed.
2. Revert a commit
git revert <commit>adds a new commit that inverts those changes- History stays intact, so it is safe on shared branches
-m 1selects the mainline parent (1 = the branch you merged into)A..Breverts a range of commits-nstages the reverts without committing, so they fold into one commit
git revert <commit> git revert -m 1 <merge-commit> git revert -n A..B git commit -m "revert A..B"
single, merge, then a range as one commit
| amend | revert | |
|---|---|---|
| History | replaces tip, new hash | adds a commit |
| Use when | local, unpushed | pushed, shared |
Reflog and Recovering Lost Work
1. What the Reflog Is
git refloglists where HEAD (and each branch) pointed over time- Entries look like
HEAD@{2}: two moves ago - Local only: never pushed to a remote
2. Recovery Recipes
| Lost | Find it | Restore |
|---|---|---|
| Bad reset / rebase | git reflog, pre-mistake entry | git reset --hard HEAD@{n} |
| Deleted branch | git reflog, last commit hash | git branch <name> <hash> |
| Dropped stash / dangling commit | git fsck --lost-found | git branch <name> <hash> |
git branch -Ddeletes the label only- Commits remain until gc, so recovery is quick
git branch -D feature git reflog # find its last hash git branch feature a1b2c3d # label is back
recreate a deleted branch
3. Time Limit
- Reachable entries expire after 90 days
- Unreachable entries expire after 30 days
Once an entry expires, gc can prune the commit for good. Run git reflog first, before anything else.
Undoing Pushed Commits Safely: Cheat Sheet
1. Decision Rule
| Situation | Do |
|---|---|
| Local, unpushed | reset or amend |
| Pushed, shared | revert |
| Panicking | reflog first |
2. Pushed Commits
- Shared branch: git revert <hash>, then a normal
git push - No force needed: history only grows
- Rewriting your own pushed branch: --force-with-lease, it refuses if the remote has new commits
- Never
resetoramendon main or any branch teammates pulled - Never bare
--force: it overwrites blind
git revert <hash>
git push
git push --force-with-leaserevert for shared, lease only for your own branch
3. Cheat Sheet
| Goal | Command |
|---|---|
| File edit | restore <f> |
| Unstage | restore --staged <f> |
| Last commit | reset --soft HEAD~1 |
| Discard all | reset --hard |
| Shared commit | revert |
| Lost commit | reflog + reset --hard HEAD@{n} |
Did anyone else pull it? Yes: revert. No: reset or amend.
Check yourself
You run git reset --soft HEAD~1. Where do the undone commit's changes end up?
- Staged in the index
--softmoves only HEAD--mixedwould leave them unstaged
You edited a file and never staged or committed it, then ran git restore file. Can the edits be recovered?
- No, Git never saw them
- reflog tracks only ref moves
A bad commit is already pushed to main. Which command is right: reset --hard or revert?
git revert <hash>, then a normal push- It adds a new commit and rewrites nothing
reset --hardwould break teammates' history
You deleted a branch with git branch -D feat. How do you get it back?
git reflog, find the branch's last commit hashgit branch feat <hash>- Only the label was deleted, commits remain until gc