Git Reset vs Revert vs Checkout

Difficulty: Intermediate

Question

Explain the differences between git reset, git revert, and git checkout. When would you use each?

Answer

These three commands all undo changes but in fundamentally different ways:

git reset: Moves the branch pointer backward, effectively erasing commits from history. Three modes: --soft (keeps changes staged), --mixed (keeps changes unstaged, default), --hard (discards all changes). Use only on local, unpushed commits because it rewrites history.

git revert: Creates a NEW commit that undoes the changes of a previous commit. History is preserved and a new commit is added. Safe for shared branches because it does not rewrite history.

git checkout (and modern git restore/git switch): Switches branches or restores files. git checkout can move HEAD to a specific commit (detached HEAD state). Modern Git split this into git switch (for branches) and git restore (for files) to avoid confusion.

Rule of thumb: Use reset for local cleanup, revert for undoing published commits, and restore/switch for navigating the working directory.

Code examples

Git Reset Modes

# Current state: A - B - C - D (HEAD)

# --soft: move HEAD back, keep changes STAGED
git reset --soft HEAD~1
# State: A - B - C (HEAD)
# D's changes are in staging area, ready to recommit
# Use case: redo a commit message or combine commits

# --mixed (default): move HEAD back, keep changes UNSTAGED
git reset HEAD~1
# State: A - B - C (HEAD)
# D's changes are in working directory, not staged
# Use case: rework changes before committing

# --hard: move HEAD back, DISCARD all changes
git reset --hard HEAD~1
# State: A - B - C (HEAD)
# D's changes are GONE (recoverable via reflog)
# Use case: completely abandon recent work

# Reset to a specific commit
git reset --hard a1b2c3d

# Reset a single file (unstage it)
git reset HEAD -- src/app.ts
# Modern equivalent:
git restore --staged src/app.ts

# RECOVERY after accidental hard reset
git reflog
# Find the lost commit hash
git reset --hard <lost-hash>

Think of reset modes as: --soft (undo commit only), --mixed (undo commit + staging), --hard (undo everything). Always check reflog if you reset --hard by mistake.

Git Revert - Safe Undo

# Revert a single commit (creates new commit)
git revert a1b2c3d
# Opens editor for the revert commit message
# Default: "Revert 'feat: add auth module'"

# Revert without auto-committing
git revert --no-commit a1b2c3d
git revert --no-commit e4f5g6h
# Both reverts are staged but not committed
# Review changes, then:
git commit -m "Revert auth and CORS changes"

# Revert a merge commit
# Merge commits have 2 parents: -m specifies which to keep
git revert -m 1 <merge-commit-hash>
# -m 1 means keep the first parent (usually main)
# -m 2 means keep the second parent (the merged branch)

# Revert a range of commits
git revert --no-commit HEAD~3..HEAD
# Reverts the last 3 commits in one batch
git commit -m "Revert last 3 commits: rollback auth feature"

# If revert has conflicts, resolve them:
# 1. Fix conflict markers
# 2. git add <files>
# 3. git revert --continue
# Or abort: git revert --abort

Revert is the safe way to undo on shared branches. It never rewrites history. Reverting a merge commit requires -m to specify which parent to keep.

Checkout, Switch, and Restore

# OLD WAY: git checkout did too many things
git checkout main             # switch branch
git checkout -b new-branch    # create + switch
git checkout -- file.txt      # restore file
git checkout a1b2c3d          # detached HEAD

# MODERN WAY: separate commands
# Switch branches
git switch main
git switch -c new-branch      # create + switch
git switch -               # switch to previous branch

# Restore files
git restore src/app.ts         # discard unstaged changes
git restore --staged src/app.ts # unstage (keep changes)
git restore --source=HEAD~1 src/app.ts  # restore from older commit
git restore --source=main -- src/app.ts # restore from another branch

# Detached HEAD state
git checkout a1b2c3d
# HEAD is not on any branch
# You can look around, make experimental commits
# To keep changes: create a branch
git switch -c keep-this-work
# To discard: just switch back
git switch main

# Restore all files to last commit (DANGEROUS)
git restore .
# Equivalent to old: git checkout -- .

Modern Git uses git switch for branches and git restore for files. This eliminates the confusion of git checkout doing multiple unrelated things.

Key points

Concepts covered

git reset, git revert, git checkout, git restore, HEAD, Detached HEAD