Handbooks / Git / Chapter 5

Undo & Recovery

7 pages · ~5 min✓ Reviewed

Builds on Rebase vs Merge. Next up: Stash & Worktrees.

GitPage 1

Undo Mental Model: Three Areas

1. Three Areas

  1. Working treefiles on disk
  2. Indexstaging, git add
  3. 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

HistoryUndo by
Local, unpublishedRewrite it: reset, amend
Pushed, sharedAdd new commits only: revert

3. Check Before Undoing

  • git status shows which area holds the change
  • git log --oneline -5 shows the latest commits
  • Run both before any undo

4. What Can Be Lost

StateRecovery
CommittedReflog ~90 days; unreachable ~30 days, until gc
Never committedNone
No way back

git restore and reset --hard on never-committed edits cannot be undone.

GitPage 2

Restore and Unstage Files

1. What restore targets

CommandEffect
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 --worktreeupdates index and worktree
No undo

Discarded uncommitted edits are unrecoverable: never committed, so not in the reflog.

2. Source and hunks

  • --source takes any commit: HEAD~2, a branch, a tag
  • -p asks per hunk: y discard, n keep, s split
  • Other hunks in the file stay untouched
bash
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

OlderModern
git checkout -- <file>git restore <file>
git reset HEAD <file>git restore --staged <file>
git checkout <c> -- <file>git restore --source=<c> <file>
GitPage 3

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
ModeMovesYour changes
--softHEADstaged
--mixed (default)HEAD + indexunstaged, still on disk
--hardHEAD + index + worktreelost

2. Undo recent commits

  • --soft HEAD~1 undoes the last commit, files stay staged
  • Ideal for redoing or squashing the last commits
  • HEAD~3 goes back three commits
bash
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

  • --hard discards uncommitted edits, so run git status first
  • ORIG_HEAD records the previous tip, so it undoes a bad merge or reset
bash
git reset --hard ORIG_HEAD

back to the tip before the merge or reset

Never-committed edits

reset --hard on edits that were never committed cannot be undone.

GitPage 4

Amend and Revert

1. Amend the last commit

  • git commit --amend replaces the last commit
  • Fix its message, or stage more files and add them without a new commit
  • --no-edit keeps the message and adds the newly staged changes
bash
git add forgotten.txt
git commit --amend --no-edit

adds the file, same message

Amend rewrites history

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 1 selects the mainline parent (1 = the branch you merged into)
  • A..B reverts a range of commits
  • -n stages the reverts without committing, so they fold into one commit
bash
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

amendrevert
Historyreplaces tip, new hashadds a commit
Use whenlocal, unpushedpushed, shared
GitPage 5

Reflog and Recovering Lost Work

1. What the Reflog Is

  • git reflog lists 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

LostFind itRestore
Bad reset / rebasegit reflog, pre-mistake entrygit reset --hard HEAD@{n}
Deleted branchgit reflog, last commit hashgit branch <name> <hash>
Dropped stash / dangling commitgit fsck --lost-foundgit branch <name> <hash>
  • git branch -D deletes the label only
  • Commits remain until gc, so recovery is quick
bash
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
Recover promptly

Once an entry expires, gc can prune the commit for good. Run git reflog first, before anything else.

GitPage 6

Undoing Pushed Commits Safely: Cheat Sheet

1. Decision Rule

SituationDo
Local, unpushedreset or amend
Pushed, sharedrevert
Panickingreflog 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 reset or amend on main or any branch teammates pulled
  • Never bare --force: it overwrites blind
bash
git revert <hash>
git push
git push --force-with-lease

revert for shared, lease only for your own branch

3. Cheat Sheet

GoalCommand
File editrestore <f>
Unstagerestore --staged <f>
Last commitreset --soft HEAD~1
Discard allreset --hard
Shared commitrevert
Lost commitreflog + reset --hard HEAD@{n}
Remember

Did anyone else pull it? Yes: revert. No: reset or amend.

GitPage 7

Check yourself

You run git reset --soft HEAD~1. Where do the undone commit's changes end up?
  • Staged in the index
  • --soft moves only HEAD
  • --mixed would 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 --hard would 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 hash
  • git branch feat <hash>
  • Only the label was deleted, commits remain until gc