GitUndoing Changes Overview

Undoing Changes in Git

Git gives you many ways to undo work — and that can be paralysing. The trick is to ask one question first: where does the bad state live right now? In your editor (working directory), staged for commit (index), already committed locally, or already pushed to a shared branch? The right command falls out of that answer.

Where can the bad state be?
  • Working directory — you edited a file but have not staged it.

  • Staging area (index) — you ran git add but have not committed yet.

  • Last local commit — you committed, but have not pushed.

  • Older local commits — several commits back, still local.

  • Pushed to a shared branch — other people may already have your bad commit.

Decision flowchart

Pick a command by where the mistake lives

Text
Where is the bad state?
│
├── Working directory only (unstaged edits)
│       └── git restore <file>              (discard edits)
│
├── Staged but not committed
│       └── git restore --staged <file>     (keep edits, unstage)
│       └── git restore --staged --worktree <file>  (throw both away)
│
├── Just committed (last commit, not pushed)
│       ├── Need to fix message/files       → git commit --amend
│       ├── Want to uncommit, keep changes  → git reset --soft HEAD~1
│       ├── Want to uncommit, unstage them  → git reset --mixed HEAD~1
│       └── Want to nuke the commit         → git reset --hard HEAD~1  (DANGER)
│
├── Older local commits (not pushed)
│       ├── Throw away history              → git reset --hard <sha>   (DANGER)
│       └── Interactively edit/squash       → git rebase -i <sha>
│
└── Already pushed to a shared branch
        └── git revert <sha>                (the only safe option)
Quick reference table

Scenario

Command

Safe on shared branch?

Discard unstaged edits to a file

git restore <file>

Yes

Unstage a file (keep edits)

git restore --staged <file>

Yes

Throw away staged + unstaged edits

git restore --staged --worktree <file>

Yes

Fix the last commit (message or files)

git commit --amend

No — rewrites history

Uncommit last commit, keep changes staged

git reset --soft HEAD~1

No

Uncommit last commit, keep changes unstaged

git reset HEAD~1

No

Throw away last commit and changes

git reset --hard HEAD~1

No

Undo an old commit by appending a new one

git revert <sha>

Yes

Remove untracked files

git clean -fd

Yes (local-only)

Recover from a bad undo

git reflog + git reset --hard HEAD@{n}

Local recovery

Commands you will meet
  • git restore — move content between trees without touching history. See the git restore page.

  • git reset — move the current branch pointer (and optionally the index/working tree). See git reset.

  • git revert — append a new commit that undoes an old one. Safe everywhere. See git revert.

  • git commit --amend — replace the most recent commit. See Amending Commits.

  • git clean — delete untracked files from the working directory. See Discarding Local Changes.

  • git reflog — your local log of every HEAD movement. See Recovery with git reflog.

The reflog is your safety net

Almost every undo operation that seems destructive can be reversed locally, because Git keeps a private log called the reflog of every position HEAD has held. If you blow away a commit with git reset --hard, the commit itself still exists in the object database for ~90 days — the reflog tells you the SHA so you can get back to it.

The two lines that saved many careers

Bash
git reflog
# 9c1a2b3 HEAD@{0}: reset: moving to HEAD~3
# 7f8d9e0 HEAD@{1}: commit: the work I just destroyed

git reset --hard HEAD@{1}    # back to safety
Warning
The reflog is **local** — never pushed. If you clone fresh or another machine ran the bad command, your reflog will not have the entries. Always work on your own clone when experimenting with destructive operations.
Mental model
Every Git undo is just *moving content between the three trees* (working dir, index, repository) or *moving the branch pointer*. Once you know where the bad state is and where you want it to be, the command name reveals itself.
Tip
Before any destructive operation, stash your work or create a throwaway branch: `git switch -c backup-before-reset`. It costs nothing and gives you an obvious recovery point that does not require digging through the reflog.