Home/Learn/Git/Undoing Changes — reset, revert, restore, stash

Undoing Changes — reset, revert, restore, stash

Intermediate
Intermediate

Git has four main undo mechanisms for different situations: restore (discard working changes), reset (move HEAD and optionally unstage/discard), revert (create a new commit that undoes a past commit), and stash (temporarily shelve work).

Overview

One of Git's superpowers is that almost nothing is permanently lost — if it was committed, it is in the object store and usually recoverable. But the commands for undoing feel confusing because there are four of them and they operate at different levels (working directory, staging area, commit history). The key insight: restore and reset --soft/--mixed are safe (they don't rewrite pushed history); reset --hard and revert have more lasting effects. Stash is neither undo nor history — it is a temporary clipboard for in-progress work.

restore — Discard Working Changes

git restore is the modern replacement for old git checkout -- file. It works at the working directory and staging area levels only — it never touches commit history.

git restore — working directory and stage
// ┌─────────────────────────────────────────────────────────────┐
// │  git restore — safe, only affects working dir or stage     │
// â””─────────────────────────────────────────────────────────────┘

# Discard changes in working directory (IRREVERSIBLE for uncommitted)
git restore src/auth.js           # restore one file from last commit
git restore src/                  # restore entire directory
git restore .                     # discard ALL working directory changes

# Unstage (move from staging → working directory, keep the change)
git restore --staged src/auth.js

# Both: unstage AND discard working directory changes
git restore --staged --worktree src/auth.js

# Restore a file to a specific commit's version
git restore --source=HEAD~3 src/auth.js

reset — Move HEAD (and optionally unstage/discard)

git reset moves the HEAD pointer (and the current branch) to a different commit. The three modes control what happens to the staging area and working directory. Use reset only on LOCAL commits you have not pushed.

git reset --soft / --mixed / --hard
//  Three modes of git reset:
//
//  HEAD~1 = one commit before current HEAD
//
//  ┌──────────────────┬──────────────┬────────────────┐
//  │ Mode             │ Staging area │ Working dir    │
//  ├──────────────────┼──────────────┼────────────────┤
//  │ --soft           │ keeps staged │ keeps changes  │
//  │ --mixed (default)│ unstages     │ keeps changes  │
//  │ --hard           │ cleared      │ DISCARDED ⚠️   │
//  â””──────────────────â”´──────────────â”´────────────────┘

# Soft: undo the commit, keep changes staged (ready to recommit)
git reset --soft HEAD~1
# → commit is gone, all changes are staged

# Mixed (default): undo commit, unstage changes (keep in working dir)
git reset HEAD~1
# → commit gone, changes are in working dir (unstaged)

# Hard: undo commit AND discard all changes — PERMANENT for working dir
git reset --hard HEAD~1  # ⚠️ working dir changes are GONE

# Go back to specific commit
git reset --hard a3f8c12

# Undo a reset using reflog (within 90 days)
git reflog
git reset --hard HEAD@{2}

revert — Safe Undo for Shared Branches

git revert creates a NEW commit that applies the inverse of a past commit. It does not rewrite history — the original commit stays. This is the correct way to "undo" commits that have already been pushed to a shared branch.

git revert — safe for shared branches
//  revert adds a new commit — history is preserved
//
//  Before:  A ── B ── C ── D  ← main (all pushed)
//
//  git revert C
//
//  After:   A ── B ── C ── D ── C'   ← C' undoes C's changes
//
//  C is still in history — this is audit-friendly and safe

# Revert the last commit
git revert HEAD

# Revert a specific commit
git revert a3f8c12

# Revert without opening the editor (use auto message)
git revert --no-edit HEAD

# Revert a range of commits (oldest first)
git revert HEAD~3..HEAD

# Revert a merge commit (must specify which parent to revert to)
git revert -m 1 <merge-commit-hash>

stash — Temporary Clipboard

stash saves your working directory and staging area changes onto a stack so you can switch branches or pull updates with a clean working directory, then pop the changes back.

git stash — temporary work storage
# Stash current work (working dir + staged)
git stash
git stash push -m "WIP: login validation refactor"

# Stash including untracked files
git stash push -u

# List all stashes
git stash list
# stash@{0}: WIP: login validation refactor
# stash@{1}: On main: quick config tweak

# Apply the latest stash (keeps it in the stack)
git stash apply

# Apply and drop from stack (pop)
git stash pop

# Apply a specific stash
git stash apply stash@{1}

# Delete a stash
git stash drop stash@{0}
git stash clear  # delete all stashes

Key Points to Remember

  • 1git restore: safe — only touches working directory or staging area, never commit history
  • 2git reset --soft: undo commit, keep changes staged. --mixed: unstage. --hard: discard everything ⚠️
  • 3git revert: creates a new "undo commit" — the only safe way to undo pushed commits
  • 4git stash: temporary clipboard — save work in progress, switch context, restore later
  • 5git reset --hard permanently discards working directory changes — verify with git status first

Interview Questions

Sign in to ask Aria
1

What is the difference between git reset and git revert?

Medium
2

When would you use git stash and what are its limitations?

Medium
3

Explain git reset --soft, --mixed, and --hard with examples.

Hard
4

How do you undo a commit that has already been pushed to a shared branch?

Medium

Ask Aria about Undoing Changes — reset, revert, restore, stash

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…