By the end of this lesson, you will:
Definition: Git recovery techniques are your safety net when things go wrong. Every developer needs these emergency skills - not if you'll need them, but when.
Remember: In Git, very little is actually "lost forever."
Git has three main "undo" areas, each requiring different approaches:
git add
ed but not committedWorking Directory → Staging Area → Repository
(git add) (git commit)
↓ Undo with: ↓ Undo with: ↓ Undo with:
git restore git restore --staged git reset/revert
Situation | Command | Safety Level | What It Does |
---|---|---|---|
Uncommitted changes | git restore |
✅ Safe | Discards working directory changes |
Staged changes | git restore --staged |
✅ Safe | Unstages files |
Recent commits | git reset |
⚠️ Careful | Moves branch pointer back |
Published commits | git revert |
✅ Safe | Creates new commit undoing changes |
Lost commits | git reflog |
✅ Safe | Shows all HEAD movements |
# See what's changed
git status
git diff
# Restore single file to last commit
git restore filename.txt
# Restore all changes in working directory
git restore .
# Restore specific directory
git restore src/
# Keep changes but create backup first
cp important-file.txt important-file.backup
git restore important-file.txt
# Unstage single file
git restore --staged filename.txt
# Unstage all files
git restore --staged .
# Check staging area
git diff --staged
# Choose which parts of a file to restore
git restore -p filename.txt
# Git will ask for each change:
# Restore this hunk [y,n,q,a,d,/,e,?]?
# y = yes, n = no, q = quit, a = all, d = don't restore any
Reset moves the branch pointer backward, effectively "undoing" commits.
Three Types of Reset:
# Undo last commit but keep changes staged
git reset --soft HEAD~1
# What this does:
# - Moves branch pointer back one commit
# - Keeps all changes in staging area
# - Useful for fixing commit messages or adding forgotten files
# Example workflow:
git reset --soft HEAD~1
# Edit files or stage more changes
git commit -m "Better commit message with all changes"
# Undo last commit, unstage changes (DEFAULT)
git reset HEAD~1
# Same as: git reset --mixed HEAD~1
# What this does:
# - Moves branch pointer back
# - Unstages changes
# - Keeps changes in working directory for editing
# Example: Split one large commit into smaller ones
git reset HEAD~1
git add specific-file.txt
git commit -m "First logical change"
git add other-files.txt
git commit -m "Second logical change"
# ⚠️ DANGER: This permanently deletes changes
git reset --hard HEAD~1
# What this does:
# - Moves branch pointer back
# - Discards staged changes
# - Discards working directory changes
# - NO UNDO without reflog
# Use cases:
# - Experimental branch you want to abandon
# - Returning to known good state
# - Emergency "undo everything" scenario
# Reset to specific commit hash
git reset --hard abc1234
# Reset to branch/tag
git reset --hard origin/main
git reset --hard v1.0.0
# Reset multiple commits
git reset --soft HEAD~3 # Undo last 3 commits
Use Reset when:
Use Revert when:
# Revert the most recent commit
git revert HEAD
# Revert specific commit
git revert abc1234
# Revert without automatic commit (review first)
git revert --no-commit HEAD
# Revert a range of commits
git revert HEAD~3..HEAD # Revert last 3 commits
Merge commits are special - they have multiple parents:
# Revert merge commit (specify parent)
git revert -m 1 HEAD # Revert to first parent
git revert -m 2 HEAD # Revert to second parent
# Usually parent 1 is main branch, parent 2 is feature branch
# Choose which changes to revert
git revert --no-commit HEAD
git restore --staged unwanted-file.txt
git commit -m "Revert most changes except unwanted-file.txt"
The reflog (reference log) records every movement of HEAD. It's your safety net for "lost" commits.
# View reflog
git reflog
# Sample output:
# a1b2c3d HEAD@{0}: commit: Add user authentication
# e4f5g6h HEAD@{1}: reset: moving to HEAD~1
# i7j8k9l HEAD@{2}: commit: Fix login bug
# m0n1o2p HEAD@{3}: merge: Fast-forward
# Find the commit you want to recover
git reflog
# Restore to specific reflog entry
git reset --hard HEAD@{2}
# Create branch from reflog entry
git branch recovered-work HEAD@{3}
# Show what changed at specific reflog entry
git show HEAD@{1}
# Show reflog for specific branch
git reflog show feature-branch
# Show reflog for all references
git reflog --all
# Restore deleted file
git restore deleted-file.txt
# Or use checkout (older Git versions)
git checkout HEAD deleted-file.txt
# Find when file was deleted
git log --oneline --follow -- deleted-file.txt
# Restore file from before deletion
git show HEAD~2:deleted-file.txt > recovered-file.txt
# Or restore directly to working directory
git restore --source=HEAD~2 deleted-file.txt
# Search for file across all commits
git log --all --full-history -- "**/filename*"
# Show file content at specific commit
git show commit-hash:path/to/file.txt
# List all files in a commit
git ls-tree -r commit-hash
# Git usually tracks renames automatically
git log --follow renamed-file.txt
# If rename tracking fails, search by content
git log -p --all -S "unique string from file"
# IMMEDIATE ACTION - if not pushed yet:
git reset --hard HEAD~1
# If already pushed - need to rewrite history:
git reset --hard HEAD~1
git push --force-with-lease origin main
# ⚠️ WARNING: Force push affects everyone!
# Better: Use git-filter-branch or BFG Repo-Cleaner
# Step 1: Don't panic, check reflog
git reflog
# Step 2: Find your lost commits
git log --oneline --all --graph --decorate
# Step 3: Recover the commit
git checkout lost-commit-hash
git branch recover-my-work # Create branch to save it
git switch main
git merge recover-my-work
# If merge hasn't been pushed:
git reset --hard HEAD~1
# If merge was pushed:
git revert -m 1 HEAD # Revert the merge
# See the divergence
git log --oneline --graph --all
# Option 1: Merge main into your branch
git switch your-branch
git merge main
# Option 2: Rebase your branch onto main
git switch your-branch
git rebase main
# Option 3: Start over with your changes
git switch main
git pull origin main
git checkout your-branch -- . # Copy files only
git switch main
git switch -c new-clean-branch
# Manual commit of your changes
# List all stashes
git stash list
# Show stash content
git stash show -p stash@{0}
# Apply specific stash
git stash apply stash@{2}
# Recover dropped stash (if reflog available)
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk
# Start bisect to find problematic commit
git bisect start
git bisect bad # Current commit is broken
git bisect good v1.0 # This version was working
# Git checks out middle commit - test it
# If broken: git bisect bad
# If working: git bisect good
# Repeat until you find the exact commit
git bisect reset # Return to original state
# Apply specific commit to current branch
git cherry-pick abc1234
# Apply multiple commits
git cherry-pick commit1 commit2 commit3
# Cherry-pick without committing (review first)
git cherry-pick --no-commit abc1234
# Create separate working directory
git worktree add ../experimental-fix
# Work in the new worktree without affecting main
cd ../experimental-fix
# Make changes safely
# Remove worktree when done
git worktree remove ../experimental-fix
git restore file.txt # Discard working changes
git restore --staged file.txt # Unstage file
git stash # Save work temporarily
git stash pop # Restore stashed work
git reset --soft HEAD~1 # Undo commit, keep changes staged
git reset HEAD~1 # Undo commit, keep changes unstaged
git reset --hard HEAD~1 # ⚠️ Undo commit, discard changes
git commit --amend # Fix last commit message/content
git revert HEAD # Safely undo last commit
git revert abc1234 # Safely undo specific commit
git revert HEAD~3..HEAD # Revert range of commits
git reflog # See all HEAD movements
git reset --hard HEAD@{3} # Go back to reflog entry
git branch recovery abc1234 # Create branch from lost commit
git fsck --lost-found # Find truly lost objects
git status
and git log --oneline --graph
git branch backup-$(date +%Y%m%d)
git diff --staged
catches mistakes# Set up helpful aliases
git config --global alias.undo 'reset HEAD~1 --mixed'
git config --global alias.amend 'commit -a --amend'
git config --global alias.wipe 'reset --hard HEAD'
git config --global alias.save 'stash push -u'
git config --global alias.load 'stash pop'
You understand:
Remember: Git is forgiving. Almost nothing is permanently lost if you know where to look. These skills will save you time, stress, and possibly your job!
Git recovery is like insurance - you hope you never need it, but when you do, you'll be grateful you learned it.