Git Rewriting History
History modification: rewriting commits, undoing mistakes, recovering from errors.
Amend Patterns
# AMEND LAST COMMIT MESSAGE
git commit --amend -m "corrected message"
git commit --amend # Opens editor
# AMEND WITH ADDITIONAL CHANGES
git add forgotten-file.txt
git commit --amend --no-edit # Add file, keep message
# AMEND AUTHOR
git commit --amend --author="Name <email@example.com>"
# AMEND DATE
GIT_COMMITTER_DATE="2024-01-15 12:00:00" git commit --amend --date="2024-01-15 12:00:00"
# CRITICAL: ONLY AMEND UNPUSHED COMMITS!
# Check if pushed:
git log --oneline origin/main..HEAD
# If shows the commit, it's NOT pushed - safe to amend
# If commit not shown, it's pushed - DON'T amend
# AMEND RULES
# ✓ Unpushed commits - safe to amend
# ✗ Pushed commits - creates divergent history
# ✗ Shared branch commits - breaks others' work
Reset Operations
# RESET MODES
# --soft: move HEAD, keep staging and working tree
# --mixed: move HEAD, reset staging, keep working tree (DEFAULT)
# --hard: move HEAD, reset staging AND working tree
# SOFT RESET (uncommit, keep changes staged)
git reset --soft HEAD~1
# Use case: "I committed too early, want to add more changes"
# MIXED RESET (uncommit, keep changes unstaged)
git reset HEAD~1 # --mixed is default
# Use case: "I want to reorganize into different commits"
# HARD RESET (uncommit, discard changes)
git reset --hard HEAD~1
# Use case: "I want to completely undo this commit"
# WARNING: Permanently discards changes!
# RESET TO SPECIFIC COMMIT
git reset --hard abc1234 # Reset to specific SHA
git reset --hard origin/main # Match remote exactly
# RESET LOCAL BRANCH TO MATCH REMOTE
# This is the "nuclear option" for syncing with remote:
git fetch origin # Get latest remote state
git reset --hard origin/main # Discard all local changes
# Use case: "My local branch is a mess, start fresh from remote"
# RESET BEHIND BY N COMMITS (catch up to remote)
git fetch origin
git log --oneline HEAD..origin/main # See what remote has
git reset --hard origin/main # Jump to remote HEAD
# UNSTAGE FILES (partial reset)
git reset HEAD file.txt # Unstage single file
git restore --staged file.txt # Modern equivalent
# RESET VS REVERT
# reset: rewrites history (moves HEAD)
# revert: creates new commit that undoes changes
# SAFE PATTERNS
# Before hard reset, save state:
git branch backup-before-reset
git reset --hard HEAD~3
# If mistake: git checkout backup-before-reset
# RESET SPECIFIC PATHS
git reset HEAD~1 -- file.txt # Reset file to previous state
git checkout HEAD~1 -- file.txt # Same effect
# RESET REFLOG (your safety net)
git reflog # See all HEAD movements
git reset --hard HEAD@{2} # Reset to 2 moves ago
Revert Patterns
# BASIC REVERT (creates new commit)
git revert abc1234 # Revert specific commit
git revert HEAD # Revert last commit
# REVERT WITHOUT AUTO-COMMIT
git revert -n abc1234 # Stage changes only
git revert --no-commit abc1234 # Same, verbose
# Useful for reverting multiple commits as one
# REVERT MULTIPLE COMMITS
git revert HEAD~3..HEAD # Revert last 3 commits
git revert -n HEAD~3..HEAD # As single commit
# REVERT MERGE COMMIT
git revert -m 1 abc1234 # Revert merge, keep main side
# -m 1: keep first parent (usually main)
# -m 2: keep second parent (merged branch)
# REVERT VS RESET
# REVERT when:
# - Commit is already pushed
# - Want to preserve history
# - Need audit trail
#
# RESET when:
# - Commit is local only
# - Want to reorganize commits
# - Cleaning up before push
# EXAMPLE: UNDO PUSHED COMMIT
git revert HEAD
git push origin main # Safe push
# EXAMPLE: UNDO MULTIPLE PUSHED COMMITS
git revert -n HEAD~2..HEAD # Stage all reversions
git commit -m "Revert: undo broken changes from commits X, Y"
git push origin main
Interactive Rebase
# START INTERACTIVE REBASE
git rebase -i HEAD~5 # Edit last 5 commits
git rebase -i main # Edit since divergence from main
git rebase -i --root # Edit all commits (careful!)
# INTERACTIVE COMMANDS
# pick = keep commit as-is
# reword = keep commit, change message
# edit = pause at commit for changes
# squash = combine with previous, keep messages
# fixup = combine with previous, discard message
# drop = remove commit entirely
# exec = run shell command
# EXAMPLE: REWORD COMMIT
# Change pick to reword:
# reword abc1234 Old message here
# Save, editor opens for new message
# EXAMPLE: SQUASH COMMITS
# Original:
# pick abc1234 Add feature
# pick def5678 Fix typo
# pick ghi9012 More fixes
# Change to:
# pick abc1234 Add feature
# squash def5678 Fix typo
# squash ghi9012 More fixes
# Result: Single commit with combined messages
# EXAMPLE: FIXUP (silent squash)
# pick abc1234 Add feature
# fixup def5678 typo fix
# fixup ghi9012 another fix
# Result: Single commit, only first message kept
# EXAMPLE: REORDER COMMITS
# Move lines in editor to reorder
# Original order: A, B, C
# New order: C, A, B
# EXAMPLE: EDIT COMMIT
# edit abc1234 Commit to change
# Save, rebase pauses. Make changes:
git add fixed-file.txt
git commit --amend
git rebase --continue
# EXAMPLE: DROP COMMIT
# drop abc1234 Bad commit
# Or delete the line entirely
# EXAMPLE: EXEC (run command)
# pick abc1234 Feature
# exec make test
# pick def5678 More work
# Rebase runs tests after first commit
# ABORT REBASE
git rebase --abort # Cancel, restore original
# CONTINUE REBASE
git rebase --continue # After resolving conflicts
# SKIP COMMIT
git rebase --skip # Drop problematic commit
Cherry-Pick Patterns
# BASIC CHERRY-PICK
git cherry-pick abc1234 # Apply single commit
git cherry-pick abc1234 def5678 # Multiple commits
git cherry-pick abc1234..def5678 # Range (exclusive start)
git cherry-pick abc1234^..def5678 # Range (inclusive)
# CHERRY-PICK OPTIONS
git cherry-pick -n abc1234 # Stage only, no commit
git cherry-pick --no-commit abc1234 # Same
git cherry-pick -x abc1234 # Add "(cherry picked from...)" to message
git cherry-pick -e abc1234 # Edit message
# CHERRY-PICK MERGE COMMIT
git cherry-pick -m 1 abc1234 # From merge, keep main parent
# CONFLICT HANDLING
git cherry-pick abc1234
# CONFLICT...
# Fix conflict, then:
git add resolved-file.txt
git cherry-pick --continue
# Or abort:
git cherry-pick --abort
# COMMON WORKFLOWS
# Backport fix to release branch
git checkout release/v1.0
git cherry-pick abc1234 # Apply fix from main
# Pull specific feature commit
git cherry-pick feature-branch~3 # Third-to-last from feature
# Create PR from specific commits
git checkout -b cherry-picked-feature
git cherry-pick abc1234 def5678 ghi9012
git push -u origin cherry-picked-feature
# CHERRY-PICK VS MERGE
# Cherry-pick: copy specific commits
# Merge: bring all branch history
# Use cherry-pick when:
# - Need specific commits only
# - Backporting fixes
# - Avoiding branch merge
Reflog and Recovery
# REFLOG BASICS
git reflog # All HEAD movements
git reflog show # Same
git reflog show HEAD # Same
git reflog show main # Branch-specific reflog
# REFLOG OUTPUT
# abc1234 HEAD@{0}: commit: Latest commit message
# def5678 HEAD@{1}: checkout: moving from feature to main
# ghi9012 HEAD@{2}: reset: moving to HEAD~1
# jkl3456 HEAD@{3}: commit: Previous commit message
# REFLOG FILTERING
git reflog --since="2 hours ago"
git reflog --until="yesterday"
git reflog -n 20 # Last 20 entries
# RECOVERY PATTERNS
# Recover from bad reset
git reset --hard HEAD~3 # Oops, too far!
git reflog # Find the commit you want
git reset --hard HEAD@{1} # Go back to before reset
# Recover deleted branch
git branch -D feature # Oops, had uncommitted work!
git reflog # Find last commit on feature
git checkout -b feature abc1234 # Recreate branch
# Recover after rebase disaster
git rebase -i main # Made a mess!
git reflog # Find pre-rebase state
git reset --hard HEAD@{5} # Restore
# Recover lost commit
git commit -m "important work"
git reset --hard HEAD~1 # Oops!
git reflog # Find the commit
git cherry-pick abc1234 # Recover it
# REFLOG EXPIRY
# Default: 90 days for reachable, 30 for unreachable
# Don't rely on reflog for long-term recovery
# FSCK FOR DEEPER RECOVERY
git fsck --lost-found # Find ALL orphaned commits
ls .git/lost-found/commit/ # View found commits
git show <sha> # Examine each
# STASH REFLOG
git reflog show stash # Stash history
# Can recover recently dropped stashes!
Rewriting Gotchas
# WRONG: Amending pushed commits
git commit -m "feature"
git push origin main
git commit --amend -m "better message"
git push origin main # Error: rejected!
# CORRECT: Only amend unpushed
git log --oneline origin/main..HEAD # Check first
# If commit shown, safe to amend
# WRONG: Hard reset pushed commits
git reset --hard HEAD~3
git push origin main # Error: non-fast-forward
# CORRECT: Use revert for pushed commits
git revert HEAD~2..HEAD
git push origin main # Clean history
# WRONG: Rebase public branches
git checkout main
git rebase feature # DON'T!
# Rewrites main, breaks everyone
# CORRECT: Rebase private branches only
git checkout feature
git rebase main # OK, feature is private
# WRONG: Force push without checking
git push --force origin main
# May overwrite others' work!
# CORRECT: Use --force-with-lease
git push --force-with-lease origin main
# Fails if remote changed since fetch
# WRONG: Forgetting reflog expires
git reset --hard HEAD~5
# ... 3 months pass ...
git reflog # Commits gone!
# CORRECT: Backup important work
git branch backup-before-reset
# Or: git stash, git tag
# WRONG: Interactive rebase across merge boundary
git rebase -i main
# Gets confusing with merge commits
# CORRECT: Use --rebase-merges if needed
git rebase -i --rebase-merges main
# WRONG: Cherry-picking merge commits without -m
git cherry-pick abc1234 # If abc1234 is a merge
# Error: commit is a merge but no -m option
# CORRECT: Specify parent
git cherry-pick -m 1 abc1234
Quick Reference
# AMEND (unpushed only!)
git commit --amend -m "new message" # Change message
git commit --amend --no-edit # Add staged files
# RESET
git reset --soft HEAD~1 # Uncommit, keep staged
git reset HEAD~1 # Uncommit, keep unstaged
git reset --hard HEAD~1 # Uncommit, discard all
git reset --hard origin/main # Match remote exactly
# REVERT (safe for pushed)
git revert abc1234 # Create undo commit
git revert -n abc1234 # Stage undo only
# INTERACTIVE REBASE
git rebase -i HEAD~5 # Edit last 5
# pick/reword/edit/squash/fixup/drop
# CHERRY-PICK
git cherry-pick abc1234 # Copy commit
git cherry-pick -n abc1234 # Stage only
git cherry-pick -m 1 abc1234 # For merges
# REFLOG (recovery)
git reflog # See all HEAD moves
git reset --hard HEAD@{2} # Go back 2 moves
# SAFETY RULES
# ✓ Amend/reset unpushed commits
# ✓ Rebase private branches
# ✓ Revert pushed commits
# ✗ Amend/reset pushed commits
# ✗ Rebase public branches
# ✗ Force push without --force-with-lease
See Also
-
History & Inspection — blame, bisect, log searching
-
Remotes — push after rewriting, force-with-lease
-
Stash — save work before destructive operations
-
Filter-Repo — repository-wide surgery