# Undo anything

> zev undo takes back the last thing you did, whether a commit, merge, rebase, deleted branch or add, without ever overwriting work that isn't saved anywhere. zev recover brings back edits a command threw away. zev op log shows every change.

Source: https://zaryn.xyz/docs/en/undo

Made a commit too early, merged the wrong branch, deleted a branch you needed? Type `zev undo`. zev records every command that changes your repository, and undo takes the last one back. Type it again to go further back. `zev redo` puts back what undo took away.

```diagram
type: cards
title: What zev undo takes back
---
✓ | A commit | the branch goes back; your changes stay, staged, ready to commit again
⑂ | A merge or rebase | the branch and your files go back to how they were
🌿 | A deleted branch | it comes back, at the same commit
↪ | A switch | you're on the branch you were on, with its files
＋ | zev add or zev rm | the file is unstaged, or back on disk
```

```bash
$ zev commit -m "Fix the login page"
$ zev undo
↶ Undid #12 zev commit -m "Fix the login page"
  main 4f2a91c0 → 9b1e07d3
  Changed your mind? zev redo
$ zev status --brief
# main
M  login.html
```

## It never throws away unsaved work

Undo changes only what the command changed. If one of those files has an edit you haven't committed or staged, undo stops and names the file, so the edit is never lost:

```text
Not undone: these files have changes that aren't saved anywhere, and zev undo would overwrite them:
  f.txt
Commit or stash them first (zev stash), or run it again with --force to overwrite them.
```

Edits to files the command didn't change are left as they are. Undoing a commit never touches your files at all: the commit's changes simply become staged again.

## Edits you threw away: zev recover

Some commands throw away edits on purpose: `zev restore <file>` discards your changes to a file, and `--force` on switch, undo or merge overwrites files. Before any of them runs, zev keeps a copy of every file whose contents aren't saved anywhere. If the command changes or removes one, zev says so and lists it under `zev recover`. If nothing was lost, nothing is listed.

```bash
$ zev restore notes.md
  Your unsaved edit to notes.md was kept: zev recover 3 brings it back
$ zev recover
#3    just now     before zev restore notes.md on main
       notes.md
$ zev recover 3
  restored  notes.md
```

`zev recover` doesn't overwrite newer work either. If you've changed the file since, the kept copy is written next to it as `notes.md.recovered-3`, so you can compare them. `--force` overwrites instead, and `zev recover 3 --show notes.md` prints the kept copy without writing anything. zev keeps the last 200 of these, and `zev gc` keeps what they need.

## Every change, and going back to any of them

`zev op log` lists what zev did, newest first, with what each command moved:

```text
#14  2 min ago    zev merge feature
       main 7c03d1e2 → 2b9f4a10
#13  5 min ago    zev branch -d old-idea
       -old-idea (was 51aa0c3e)
#12  9 min ago    zev commit -m "Fix the login page"   (undone)
```

`zev op restore <n>` takes the repository back to how it was right after operation `n`: its branches, tags, what was staged, and your files. That is itself an operation, so `zev undo` takes it back.

```diagram
type: history
title: Undo and redo move along the operations, never deleting one
start: Operations
---
branch ops | accent
commit A ops | #1 zev commit "Start"
commit B ops | #2 zev merge feature
commit C ops | #3 zev branch -d old | tone bad | info A mistake
step zev undo | #3 is taken back: the branch is there again
commit D ops | #4 zev undo (#3) | tone good
step zev redo | would take it away again, until you do something new
```

## For scripts and AI agents

`zev undo`, `zev redo` and `zev op log` take `--json` and `--brief` like the other everyday commands. An agent can try a change, check it, and take it back with one command. When a file is in the way, the answer is data, not a sentence:

```bash
$ zev undo --json
{"error":"files have changes that aren't saved anywhere","code":"unsaved_changes","files":["f.txt"]}
```

## How this differs from Git

Git keeps a reflog for each branch, but going back means finding the right entry and choosing between `git reset --soft`, `--mixed` and `--hard`; the wrong one discards work. A deleted branch, a merge and a rebase each need a different recipe. In zev it's one command for all of them, it never discards work that isn't saved anywhere, and `zev op log` shows the whole repository's history of changes in one place.

| In Git | In zev |
|---|---|
| `git reset --soft HEAD~1` (undo the last commit) | `zev undo` |
| `git reflog`, then `git reset --hard HEAD@{2}` (undo a merge or rebase) | `zev undo` |
| `git reflog`, then `git branch name <hash>` (bring back a deleted branch) | `zev undo` |
| `git reflog` | `zev op log` |

`zev gc` keeps every commit the operation log needs, so an undone commit can always come back with `zev redo` or `zev op restore`.

Already pushed the commit you want to undo? Others may have it, so make a new commit that reverses it instead: [How do I…? › Undo a commit I already made](/docs/en/how-do-i).
