Git - Undoing Changes

Intermediate

Everyone makes mistakes, and Git provides powerful tools to undo changes at any stage. Understanding these commands can save you from disasters and help you maintain clean project history.

The Three Levels of Undo

Git has different undo commands depending on where your changes are:

Working Directory  β†’  Staging Area  β†’  Repository
    (modified)         (staged)         (committed)
        ↓                 ↓                ↓
   git checkout      git reset        git revert
   git restore       git restore      git reset

Undoing Working Directory Changes

Discard Uncommitted Changes

Modern Git (2.23+):
# Discard changes to specific file  
git restore filename.txt

# Discard all changes
git restore .

# Restore file from specific commit
git restore --source=HEAD~1 filename.txt
Classic Git:
# Discard changes to specific file
git checkout -- filename.txt

# Discard all changes  
git checkout -- .
Warning: These commands permanently discard uncommitted changes. They cannot be recovered!

Interactive Restoration

# Interactively choose what to restore
git restore -p filename.txt

# Shows each change and asks:
# Discard this hunk [y,n,q,a,d,e,?]?

Undoing Staged Changes

Unstaging Files

Modern Git (2.23+):
# Unstage specific file
git restore --staged filename.txt

# Unstage all files
git restore --staged .
Classic Git:
# Unstage specific file
git reset HEAD filename.txt

# Unstage all files
git reset HEAD

Note: Unstaging doesn't discard changesβ€”files remain modified in working directory.

Understanding git reset

git reset is powerful but dangerous. It has three modes:

Soft Reset

# Move HEAD, keep staging and working directory
git reset --soft HEAD~1

Use when: You want to redo the last commit with different message or add more files.

Before reset --soft:
A---B---C (HEAD)
Staging: (empty)
Working: (clean)
After reset --soft HEAD~1:
A---B (HEAD)
Staging: (changes from C)
Working: (clean)

Mixed Reset (Default)

# Move HEAD, reset staging, keep working directory
git reset HEAD~1
git reset --mixed HEAD~1  # Same as above

Use when: You want to uncommit changes but keep them in working directory for editing.

After reset --mixed HEAD~1:
A---B (HEAD)
Staging: (empty)
Working: (changes from C, modified)

Hard Reset (Destructive)

# Move HEAD, reset staging and working directory
git reset --hard HEAD~1
⚠️ Destructive: This permanently discards all changes. Use with extreme caution!
After reset --hard HEAD~1:
A---B (HEAD)
Staging: (empty)
Working: (clean, changes from C lost!)

Safe Undoing with git revert

git revert is the safe way to undo commitsβ€”it creates new commits that reverse changes.

Basic Revert Operations

# Revert the last commit
git revert HEAD

# Revert specific commit
git revert a1b2c3d

# Revert without auto-commit (review first)
git revert --no-commit a1b2c3d
Revert output:
[main d4e5f6g] Revert "Add broken feature"
 2 files changed, 0 insertions(+), 15 deletions(-)

Reverting Merge Commits

# Revert merge commit (specify parent)
git revert -m 1 merge-commit-hash

# -m 1 reverts to first parent (usually main branch)
# -m 2 reverts to second parent (usually feature branch)

Revert vs Reset

Aspectgit revertgit reset
Safetyβœ… Safe for shared history❌ Dangerous for shared history
Historyβœ… Preserves history❌ Rewrites history
MethodCreates new commitMoves HEAD pointer
Use casePublic branchesPrivate/local work

Practical Undo Scenarios

Scenario 1: Fix Last Commit Message

# Wrong commit message
git commit -m "Fix big in login"

# Fix it immediately
git commit --amend -m "Fix bug in login"

Scenario 2: Add Forgotten Files to Last Commit

# Made commit but forgot a file
git add forgotten-file.txt
git commit --amend --no-edit

Scenario 3: Undo Last Commit (Keep Changes)

# Committed too early, want to continue working
git reset --soft HEAD~1

# Now changes are staged, continue editing
echo "more changes" >> file.txt
git add file.txt
git commit -m "Complete feature implementation"

Scenario 4: Completely Remove Last Commit

# Last commit was completely wrong
git reset --hard HEAD~1

# Or if commit was pushed, use revert
git revert HEAD

Scenario 5: Go Back Multiple Commits

# Go back 3 commits, keep changes for editing
git reset HEAD~3

# Or completely remove last 3 commits (dangerous!)
git reset --hard HEAD~3

Recovery from Mistakes

The Reflog Lifesaver

# View recent HEAD movements
git reflog
Reflog output:
d4e5f6g (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
a1b2c3d HEAD@{1}: commit: Add user authentication
b2c3d4e HEAD@{2}: commit: Update documentation
c3d4e5f HEAD@{3}: commit: Fix styling issues
e5f6g7h HEAD@{4}: commit: Initial setup
# Recover "lost" commits
git reset --hard HEAD@{1}

# Or create new branch from lost commit
git branch recovery-branch a1b2c3d

Finding Lost Content

# Find commits with specific content
git log --all --full-history -S"lost content"

# Show unreachable commits
git fsck --lost-found

Advanced Undo Techniques

Interactive Rebase for Complex Undo

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

# Options in editor:
# pick = use commit
# edit = stop for editing  
# drop = remove commit
# squash = combine with previous

Cherry-pick to Undo Selective Changes

# Apply only specific commits after reset
git reset --hard HEAD~5
git cherry-pick a1b2c3d  # Keep only this commit
git cherry-pick c3d4e5f  # And this one

Bisect to Find Problem Commits

# Find commit that introduced bug
git bisect start
git bisect bad          # Current commit has bug
git bisect good v1.0.0  # This version was good

# Git will guide you through binary search
# Mark each test as good or bad
git bisect good   # or git bisect bad

# When found:
git bisect reset
git revert bad-commit-hash

File-Level Operations

Restore File from Specific Commit

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

# Restore file from different branch
git restore --source=main filename.txt

# Restore and stage immediately
git restore --source=HEAD~1 --staged filename.txt

Show File History for Recovery

# See all versions of a file
git log --follow -p -- filename.txt

# Show file at specific commit
git show HEAD~3:filename.txt

# Restore file to specific version
git show HEAD~3:filename.txt > filename.txt

Undoing Pushed Changes

Safe Undo for Shared Branches

# Never use reset on pushed commits!
# Use revert instead:

git revert HEAD           # Undo last commit
git push origin main      # Safe to push

# For multiple commits:
git revert HEAD~2..HEAD   # Revert last 2 commits
git push origin main

Emergency Force Push (Team Coordination Required)

# Only if you coordinate with entire team!
git reset --hard HEAD~1
git push --force-with-lease origin main

# Tell team to:
# git fetch origin
# git reset --hard origin/main
Force push coordination: Everyone on the team must reset their local branches. This can destroy other people's work!

Prevention is Better than Cure

Safe Working Practices

  • βœ… Commit frequently with small changes
  • βœ… Use feature branches for experiments
  • βœ… Review changes before committing
  • βœ… Use git status and git diff regularly
  • βœ… Test before pushing to shared branches

Backup Strategies

# Create backup branch before risky operations
git branch backup-before-rebase

# Push backup to remote
git push origin backup-before-rebase

# After successful operation, clean up
git branch -d backup-before-rebase
git push origin --delete backup-before-rebase

Summary

You now understand Git undo operations:

  • βœ… git restore discards working directory changes
  • βœ… git reset moves HEAD with different levels of destruction
  • βœ… git revert safely undoes commits in shared history
  • βœ… git reflog helps recover "lost" commits
  • βœ… Different scenarios require different undo strategies
  • βœ… Prevention through good practices is better than recovery
  • βœ… Never use destructive operations on shared/pushed commits

Next Steps

Now that you can undo changes safely, let's learn about temporarily storing changes:

β†’ Git - Stashing

Practice Tip: Create a test repository and practice these undo operations. Understanding the differences between reset modes is crucial for safe Git usage.