All posts
6 min read

How to Undo Almost Anything in Git (Including an AI Agent's Mess)

An agent committed to the wrong branch, rewrote files you needed, or ran a reset it shouldn't have. Git can almost always get your work back. Which undo command fits which situation — restore, revert, reset, and the reflog that rescues 'deleted' commits — explained with the exact commands.

developer experiencetoolingAI agentsgetting started

Working with AI coding agents means more commits, more branches, and more moments of "wait, what just happened to my files?" The good news: git almost never truly loses committed work. The bad news: git has several different undo commands, and using the wrong one can make things worse.

This is a guide to picking the right one. For each situation, the command, what it does, and how dangerous it is.

Before any of this: stop and look. Run these first. They change nothing and tell you where you are:

git status          # what's changed, what's staged, which branch
git log --oneline -10   # recent commits on this branch
git diff            # exactly what changed in your files

And if you're about to try something you're unsure of, make a safety copy of where you are:

git branch backup-before-undo

A branch is just a bookmark. It costs nothing and makes almost everything below recoverable.

"I changed a file and want it back the way it was"

Uncommitted changes to one file:

git restore path/to/file.ts

This throws away your uncommitted changes to that file. They're gone — git never saw them, so git can't bring them back. To discard everything uncommitted: git restore .

If you might want the changes later, stash them instead:

git stash           # put all uncommitted changes aside
git stash pop       # bring them back

"I staged something I didn't mean to"

git restore --staged path/to/file.ts

Unstages the file. Your changes stay in the file; they're just no longer lined up for the next commit. Completely safe.

"I want a file back from an earlier commit"

git restore --source=HEAD~3 path/to/file.ts

Puts the file back exactly as it was three commits ago (or use a commit ID instead of HEAD~3). Useful when an agent rewrote a file you wanted to keep as it was. Only that file changes.

"The last commit message is wrong, or I forgot a file"

If you haven't pushed yet:

git add forgotten-file.ts
git commit --amend

This replaces the last commit with a corrected one. Don't amend a commit you've already pushed to a shared branch — it rewrites history other people may have.

"I need to undo a commit that's already pushed"

git revert <commit-id>

revert creates a new commit that does the opposite of the old one. History is preserved; nothing is rewritten; it's safe on shared branches. This is the right way to undo something on main.

"I want to throw away my last few commits"

reset moves your branch back to an earlier commit. It comes in three strengths:

git reset --soft HEAD~2    # undo 2 commits, keep their changes staged
git reset --mixed HEAD~2   # undo 2 commits, keep their changes unstaged (the default)
git reset --hard HEAD~2    # undo 2 commits AND discard their changes

--soft and --mixed are gentle: the commits go away, the work stays in your files, and you can recommit it differently. That's a great way to squash an agent's twelve tiny commits into one.

--hard also discards the work in your files. Uncommitted changes are lost for good. The commits themselves can still be recovered — see the reflog below — but make a backup branch first anyway.

Only reset commits that haven't been pushed to a shared branch.

"I committed to the wrong branch"

Say an agent committed to main when it should have been on a feature branch, and you haven't pushed:

git branch feature/new-thing     # bookmark the commits on a new branch
git reset --hard origin/main     # move main back to match the remote
git switch feature/new-thing     # carry on where the work is

The commits now live on the feature branch, and main is clean.

"I need one commit from another branch"

git cherry-pick <commit-id>

Copies that single commit onto your current branch. Useful when one agent's fix belongs on a different branch than the rest of its work.

"I deleted a branch" or "reset --hard took my commits"

This is the one people think is hopeless. It usually isn't. Git keeps a private log of everywhere your branch pointer has been: the reflog.

git reflog

You'll see a list like:

a1b2c3d HEAD@{0}: reset: moving to HEAD~3
e4f5g6h HEAD@{1}: commit: add team billing
i7j8k9l HEAD@{2}: commit: add team model

Find the entry just before the mistake — here, e4f5g6h, before the reset — and put a branch on it:

git branch rescued e4f5g6h

Your "lost" commits are back. The reflog is local to your machine, and by default keeps entries for commits no branch points to for 30 days — so act reasonably soon, but you have time.

"I merged and wish I hadn't"

Not pushed yet: git reset --hard ORIG_HEAD (git sets ORIG_HEAD before a merge). Already pushed: revert the merge with git revert -m 1 <merge-commit-id>, which undoes it with a new commit.

What git can't undo

  • Uncommitted changes you discarded with restore or reset --hard. Git never had them.
  • Files that were never committed at all, including ones listed in .gitignore — like .env or a local database file.
  • Anything outside the repository: database changes, installed packages, deployed releases, emails sent.
  • A secret you pushed. Removing it from history doesn't un-leak it. Rotate the secret. (How to Keep API Keys Out of an AI-Built App.)

Which is why the best undo strategy is committing often. A commit is a save point. The more of them there are, the smaller any mistake is.

Rules for working with agents

  1. Commit before handing an agent a big task, so there's a clean point to come back to.
  2. Have agents work on branches, never directly on main.
  3. Don't let agents force-push or rewrite shared history. Deny git push --force and git reset --hard in their permission rules, or require approval. (Running Claude Code Unattended shows how.)
  4. Push regularly. A pushed branch survives the loss of your machine.
  5. Know your agent's own undo, and its limits. Claude Code's /rewind restores edits made with its file tools, but not changes made by shell commands — git covers those. See Claude Code Checkpoints.

Quick reference

Situation Command Safe?
Discard changes to a file git restore <file> Loses uncommitted changes
Unstage a file git restore --staged <file> Yes
Get a file from an old commit git restore --source=<commit> <file> Yes
Fix last commit (unpushed) git commit --amend Yes, if unpushed
Undo a pushed commit git revert <commit> Yes
Undo commits, keep the work git reset --soft HEAD~N Yes, if unpushed
Undo commits and the work git reset --hard HEAD~N Loses uncommitted changes
Recover "lost" commits git reflog then git branch <name> <id> Yes

EasySpawn keeps GitHub as the canonical home of your code: Claude Code works on branches in a persistent workspace, commits and pushes, and opens pull requests — so every change has a save point. See how it works or join the waitlist.

Related: Running Claude Code Agents in Parallel With Git Worktrees · How to Review a Pull Request Written by an AI Agent · Git and GitHub for Beginners · Merge Conflicts Explained

Keep reading