Skip to main content

Undoing Changes

Comprehensive guide to undoing changes at every stage of the Git workflow, from working directory to published commits.

Understanding Git States

The Three Trees

Git manages three "trees" or states where your files can exist:

# Working Directory (modified files)
git status # Shows modified files

# Staging Area/Index (staged files)
git status # Shows staged files

# Repository (committed files)
git log --oneline # Shows committed changes

File States Overview

# Check current state
git status

# File lifecycle states:
# Untracked → Modified → Staged → Committed
# Each state has different undo methods

Undoing Working Directory Changes

Discard Unstaged Changes

# Modern syntax (Git 2.23+)
git restore filename.txt # Discard changes in one file
git restore . # Discard all changes
git restore src/ # Discard changes in directory

# Traditional syntax
git checkout -- filename.txt # Discard changes in one file
git checkout -- . # Discard all changes
git checkout HEAD -- filename.txt # Restore from HEAD

# Interactive selection
git checkout -p # Choose hunks to discard
git restore -p filename.txt # Modern interactive restore

Restore from Specific Commit

# Restore file from specific commit
git restore --source=HEAD~1 filename.txt
git restore --source=abc123 filename.txt

# Traditional method
git checkout HEAD~1 -- filename.txt
git checkout abc123 -- filename.txt

# Restore from different branch
git restore --source=feature-branch filename.txt
git checkout feature-branch -- filename.txt

Clean Untracked Files

# Remove untracked files (dry run first)
git clean -n # Show what would be removed
git clean -f # Remove untracked files
git clean -fd # Remove files and directories
git clean -fx # Remove ignored files too

# Interactive clean
git clean -i # Interactive mode

# Clean specific patterns
git clean -f "*.tmp" # Remove specific file types
git clean -f build/ # Clean specific directory

Undoing Staged Changes

Unstage Files

# Modern syntax (Git 2.23+)
git restore --staged filename.txt # Unstage one file
git restore --staged . # Unstage all files

# Traditional syntax
git reset HEAD filename.txt # Unstage one file
git reset HEAD # Unstage all files
git reset # Same as reset HEAD

# Reset to specific commit
git reset abc123 filename.txt # Unstage and reset to commit

Partial Unstaging

# Interactive unstaging
git reset -p # Choose hunks to unstage
git restore --staged -p filename.txt # Modern interactive unstage

# Unstage specific lines (using editor)
git add -p filename.txt # First, review what's staged
git reset -p filename.txt # Then selectively unstage

Undoing Commits

Soft Reset (Keep Changes)

# Undo last commit, keep changes staged
git reset --soft HEAD~1

# Undo multiple commits, keep changes staged
git reset --soft HEAD~3
git reset --soft abc123

# Check what was undone
git status # Changes will be staged
git diff --cached # See what's staged

Mixed Reset (Default)

# Undo last commit, keep changes unstaged
git reset HEAD~1 # Default is --mixed
git reset --mixed HEAD~1

# Undo to specific commit
git reset abc123

# Check what was undone
git status # Changes will be unstaged
git diff # See what's in working directory

Hard Reset (Discard Changes)

# WARNING: This discards all changes permanently
git reset --hard HEAD~1 # Undo last commit, discard changes
git reset --hard HEAD~3 # Undo 3 commits, discard changes
git reset --hard abc123 # Reset to specific commit

# Reset branch to match remote
git reset --hard origin/main

# Emergency: Reset to last known good state
git reset --hard HEAD@{1} # Use reflog to find state

Revert Commits (Safe Public Undo)

# Revert last commit (creates new commit)
git revert HEAD

# Revert specific commit
git revert abc123

# Revert merge commit
git revert -m 1 abc123 # -m 1 means first parent

# Revert multiple commits
git revert HEAD~3..HEAD # Revert last 3 commits
git revert --no-commit HEAD~3..HEAD # Revert but don't commit yet

# Revert range of commits
git revert abc123..def456

Advanced Revert Options

# Revert without committing
git revert --no-commit abc123 # Stage revert, don't commit

# Edit revert commit message
git revert --edit abc123

# Revert with custom message
git revert abc123 -m "Revert problematic feature"

# Abort revert if conflicts
git revert --abort

# Continue revert after resolving conflicts
git revert --continue

Modifying Recent Commits

Amend Last Commit

# Amend commit message only
git commit --amend

# Amend commit message without editor
git commit --amend -m "New commit message"

# Amend commit with new changes
git add forgotten-file.txt
git commit --amend # Adds file to last commit

# Amend without changing message
git commit --amend --no-edit

# Amend author information
git commit --amend --author="New Author <new@email.com>"

Fixup and Squash

# Create fixup commit
git commit --fixup=abc123 # Creates commit to fix abc123

# Create squash commit
git commit --squash=abc123 # Creates commit to squash with abc123

# Then use interactive rebase to apply
git rebase -i --autosquash HEAD~5

Interactive Rebase for History Editing

Basic Interactive Rebase

# Rebase last 3 commits interactively
git rebase -i HEAD~3

# Rebase from specific commit
git rebase -i abc123

# Available actions in interactive rebase:
# pick (p) = use commit
# reword (r) = use commit, but edit message
# edit (e) = use commit, but stop for amending
# squash (s) = use commit, but meld into previous
# fixup (f) = like squash, but discard message
# drop (d) = remove commit

Common Rebase Scenarios

# Squash multiple commits into one
git rebase -i HEAD~3
# Change 'pick' to 'squash' for commits to combine

# Reorder commits
git rebase -i HEAD~3
# Reorder lines in the editor

# Edit old commit
git rebase -i HEAD~3
# Change 'pick' to 'edit' for commit to modify
# Make changes, then:
git add .
git commit --amend
git rebase --continue

# Split a commit
git rebase -i HEAD~3
# Change 'pick' to 'edit'
git reset HEAD~1 # Unstage the commit
# Now stage and commit separately
git add file1.txt
git commit -m "First part"
git add file2.txt
git commit -m "Second part"
git rebase --continue

Emergency Recovery

Using Reflog

# View reflog (history of HEAD changes)
git reflog

# View reflog for specific branch
git reflog show branch-name

# Recover lost commits
git reflog # Find lost commit hash
git reset --hard abc123 # Reset to lost commit

# Recover deleted branch
git reflog # Find commit where branch was
git branch recovered-branch abc123

Recovering from Hard Reset

# If you accidentally did git reset --hard
git reflog # Find the commit before reset
git reset --hard HEAD@{1} # Reset to previous state

# Alternative: use ORIG_HEAD
git reset --hard ORIG_HEAD # Reset to state before last reset

Finding Lost Content

# Find dangling commits
git fsck --lost-found

# Search for content in all commits
git log --all --grep="search term"
git log --all -S "code snippet"

# Search in reflog
git log --walk-reflogs --grep="search term"

Undoing Published Changes

Revert Public Commits

# NEVER use reset on public/shared branches
# Use revert instead:

git revert HEAD # Revert last commit
git revert abc123 # Revert specific commit
git push origin main # Push the revert

# Revert merge
git revert -m 1 merge-commit-hash

Force Push with Caution

# If you must rewrite public history (dangerous):
git reset --hard HEAD~1
git push --force-with-lease origin branch-name

# Safer than --force, checks for updates
git push --force-with-lease

# Only if you're sure no one else is using the branch
git push --force

Selective Undoing

Cherry-pick Reverse

# Apply inverse of a commit
git revert abc123

# Apply specific commit to current branch
git cherry-pick abc123

# Cherry-pick without committing
git cherry-pick --no-commit abc123

# Cherry-pick range
git cherry-pick abc123..def456

Partial File Reset

# Reset specific file to specific commit
git checkout abc123 -- filename.txt
git restore --source=abc123 filename.txt

# Reset specific lines (interactive)
git checkout -p abc123 -- filename.txt
git restore --source=abc123 -p filename.txt

Stash as Undo Tool

Using Stash for Temporary Undo

# Temporarily undo all changes
git stash # Save and remove changes
git stash pop # Restore changes later

# Stash with message
git stash push -m "Temporary undo for testing"

# Stash specific files
git stash push filename.txt

# Create branch from stash
git stash branch new-branch stash@{0}

Best Practices for Undoing

Safety Guidelines

# Always check what you're undoing
git status # Before any undo operation
git log --oneline -5 # See recent commits
git diff # See current changes

# Use safe commands when possible
git revert # Instead of reset for public commits
git reset --soft # Instead of --hard when possible
git clean -n # Dry run before cleaning

# Backup before major operations
git branch backup-$(date +%Y%m%d)
git tag backup-tag HEAD

When to Use Each Method

# Use restore/checkout for:
# - Discarding working directory changes
# - Unstaging files

# Use reset for:
# - Undoing local commits (not pushed)
# - Moving branch pointer

# Use revert for:
# - Undoing public/pushed commits
# - Safe undo that preserves history

# Use amend for:
# - Fixing the last commit only
# - Adding forgotten files to last commit

# Use interactive rebase for:
# - Cleaning up commit history before pushing
# - Squashing related commits

Recovery Checklist

# If something goes wrong:
1. Don't panic - Git rarely loses data
2. Check reflog: git reflog
3. Check status: git status
4. Look for dangling commits: git fsck --lost-found
5. Check if ORIG_HEAD helps: git show ORIG_HEAD
6. Search for lost content: git log --all -S "content"

Common Scenarios

"Oh No" Moments and Solutions

# "I committed to the wrong branch"
git reset --soft HEAD~1 # Undo commit, keep changes
git stash # Save changes
git switch correct-branch # Switch to right branch
git stash pop # Apply changes
git commit # Commit on correct branch

# "I committed too early"
git reset --soft HEAD~1 # Undo commit, keep changes staged
# Make additional changes
git add .
git commit # Commit everything together

# "I have a typo in my commit message"
git commit --amend # Fix the message

# "I accidentally deleted a file"
git restore filename.txt # Restore from last commit
git checkout HEAD~1 -- filename.txt # Restore from earlier commit

# "I merged the wrong branch"
git reset --hard HEAD~1 # If merge not pushed
git revert -m 1 HEAD # If merge was pushed

Master undoing changes before exploring Staging & Stashing and Troubleshooting.