# Checkpoints and branching: undo Cursor agent changes safely

URL: https://developertoolkit.ai/en/cursor-ide/build/checkpoints-branching/
Description: Pair Cursor checkpoints with Git branches and commits so you can undo one agent turn, one file or a whole experiment, and keep only verified changes.

Cursor checkpoints are automatic local snapshots of the files Cursor's agent changes, used to roll back an agent turn from the chat. A checkpoint restores files only: it does not reverse commands and is not a commit. The safe pattern pairs checkpoints with Git, with a commit before each agent task and a branch per experiment.

This page is for **developers** who let Cursor's agent make multi-file changes. You ask the agent to refactor the login flow. It rewrites six files, then also "tidies" the database connection pool you never mentioned. You press undo a few times, the edits are interleaved across files, and in frustration you run `git checkout .` and lose the login changes you wanted as well. Every step of that is avoidable.

## What a checkpoint-and-branch routine gives you

- A five-step routine that makes every agent turn cheap to throw away.
- A decision table that picks the right undo: checkpoint, `git restore`, `git reset` or deleting a branch.
- A single-command scope check and a gate run that tell you whether to keep a turn, without reading every line of the diff.
- Three copy-paste prompts: a scoped task, a plan-first request and a redo after a restore.
- A branch-naming scheme that avoids the `cannot lock ref` error the classic "micro-branch" advice runs into.

## What Cursor documents, and what this page relies on

Cursor's checkpoints are stored locally and separate from Git, and restoring one reverts files only (Cursor agent overview, checked 2026-10-12). Cursor's documentation site could not be reached on 26 September 2026, so the rest of this page leans on Git, which you can verify locally, and assumes as little as possible about checkpoint internals.

| Feature | What Cursor documents | Last checked | What this page assumes |
| --- | --- | --- | --- |
| **Checkpoints** | A Checkpoints page in the agent chat docs describes automatic snapshots of the agent's changes: "Checkpoints are stored locally and separate from Git", and "Restoring a checkpoint reverts files only" | 2026-10-12 | A checkpoint can put back the files the agent changed in a turn. Nothing about how long it lasts or what else it covers |
| **Worktrees** | "Worktrees let Agent work in isolated Git checkouts." | 28 August 2026 | A worktree is an ordinary Git worktree with its own branch |
| **`/rewind` in the Cursor CLI** | Listed among the CLI slash commands, without a definition | 28 August 2026 | Nothing. This page covers the editor |

Because the details can change between Cursor releases, run the short scope test in the next section once on your own version. It tells you exactly what a restore touches.

## How far does a Cursor checkpoint reach?

Treat a checkpoint as a quick undo for the **last few agent turns in the current chat**. Treat everything you want to keep as a commit. Two limits follow from what a checkpoint is, whatever your Cursor version:

- **It restores files, not the world.** If the agent ran a database migration, installed a package, deleted a Docker volume or pushed a branch, restoring files does not reverse any of it.
- **It restores the snapshot, not your intent.** If you edited a file by hand after the agent's turn, restoring that turn's checkpoint puts the file back to the snapshot. Your manual edit goes with it unless you committed it first.

To see what your version does, run this test once in a scratch branch:

1. Create a throwaway branch: `git switch -c scratch/checkpoint-test`.
2. Ask the agent: "Add a comment line to `README.md`, create `tmp-checkpoint-test.txt` containing `hello`, then run `echo ran >> tmp-command-log.txt` in the terminal."
3. Edit `README.md` by hand and add a second line of your own.
4. Restore the checkpoint from before that agent turn in the chat.
5. Run `git status --short` and `cat README.md`. Note which of the three files came back, disappeared or kept their changes, and whether your manual line survived.
6. Clean up: `git switch -` and `git branch -D scratch/checkpoint-test`, then delete any leftover test files.

Whatever the result, the routine below does not depend on it: Git is the layer you trust.

## Set up a checkpoint-and-branch routine for every agent task

Checkpoints cover a single turn; Git covers everything else. The routine below adds a few seconds per task and makes every turn disposable.

1. **Start clean.** Run `git status --short`. If it prints anything, commit or stash it (`git stash push -m "before agent"`) so the agent's changes are the only changes in the working tree.

2. **Branch per task.** Run `git switch -c agent/login-refactor`. One task per branch means that deleting the branch is always a complete undo.

3. **Give the agent a scope and a check.** Name the directories it may touch and the command that proves the change works. Use the scoped-task prompt below. For risky changes, ask for a plan first; [agent modes in Cursor](/en/cursor-ide/build/agent-modes-deep-dive/) explains when Plan mode is the better start.

4. **Check the turn before you keep it.** Run the scope check and your gates (next section). If a turn fails, restore its checkpoint or discard the file with Git, then re-prompt.

5. **Commit every green turn.** `git add -A && git commit -m "agent: extract session token validation"`. Each commit is a restore point that survives restarts, new chats and other tools. Squash when you open the pull request if your team prefers one commit per change.

**Copy-paste prompt: scoped agent task with a stop condition**

```text
Refactor the login flow so session-token validation lives in src/auth/session.ts
instead of being duplicated in src/auth/login.ts and src/auth/refresh.ts.

Scope: you may edit files under src/auth/ and tests/auth/ only. Do not touch
src/db/, configuration files or package.json. If the change seems to need a file
outside that scope, stop and tell me which file and why instead of editing it.

Before you start, run `git status --short`. If it prints anything, stop and tell me.
After your change, run `npm test -- tests/auth` and `npm run typecheck`.
Finish with: the list of files you changed, the commands you ran and their results.
```

**Copy-paste prompt: plan before touching critical code**

```text
Before you change anything, write a plan for adding rate limiting to POST /api/login.
List every file you intend to create or modify, what changes in each, which
existing tests cover it and which new test you will add first. Name anything
that has a side effect outside the files (migrations, new packages, config).
Do not edit any file until I reply "approved".
```

## Keep or discard an agent turn without reading every line

You decide whether to keep a turn from evidence, not from reading the whole diff. Three checks, in this order, answer the question quickly.

**1. Did the agent stay in scope?** This command lists every changed or new file outside the paths you allowed. It prints `scope OK` when there are none. It runs in your terminal at the repository root:

```bash
{ git diff --name-only HEAD; git ls-files --others --exclude-standard; } \
  | grep -v -E '^(src/auth/|tests/auth/)' || echo "scope OK"
```

Any file it prints is a change you did not ask for. Discard it with `git restore --source=HEAD -- src/db/pool.ts` (tracked file) or delete it (new file), then tell the agent why.

**2. Do the gates pass on the working tree?** Run the same commands CI runs, not the agent's summary of them: `npm run typecheck && npm run lint && npm test`. The agent's "all tests pass" is a claim; the exit code is evidence.

**3. Does the new test prove the behaviour?** A test that passes against the old code proves nothing. Stash the implementation files, keep the test, and run it:

```bash
git stash push -u -- src/auth/     # set aside the implementation, keep tests/auth/
npm test -- tests/auth             # the new test must FAIL here
git stash pop                      # bring the implementation back
npm test -- tests/auth             # and PASS here
```

Check that it fails on an assertion about the behaviour, not on a missing import. For a pure refactor there is no new behaviour to prove; the existing tests passing before and after is the evidence. Use the fail-then-pass run when the turn adds behaviour, such as the rate-limiting task.

If the test passes in both runs, ask the agent for a test that fails without the change. Write the expected behaviour down first with [acceptance criteria](/en/workflows/plan/acceptance-criteria/) and the [test-first loop in Cursor](/en/cursor-ide/test/testing-tdd/).

Only then read the diff, and only where the checks cannot speak: public interfaces, security-relevant code and anything the tests do not exercise. The pull request carries the scope result, the gate output and the fail-then-pass test run as its [evidence bundle](/en/workflows/ship/evidence-bundle/). You approve the change as the branch owner, CI re-runs the gates, and the reviewer follows your team's [code review workflow in Cursor](/en/cursor-ide/ship/code-review/).

## Which undo do you need?

Pick the smallest undo that removes the bad change and keeps the good one.

| Situation | Use | Command or action |
| --- | --- | --- |
| The last agent turn is wrong and you have not committed since | Checkpoint restore | Restore the checkpoint before that turn in the chat |
| One or two files are out of scope; the rest is good | Git, per file | `git restore --source=HEAD -- PATH` for tracked files; delete new files |
| Part of one file is good, part is not | Git, per hunk | `git restore -p -- PATH` and answer `y` for each hunk to discard |
| A turn you already committed is wrong | Git, new commit | `git revert COMMIT` keeps history intact |
| The last few commits on your agent branch are wrong | Git, move the branch | `git reset --hard COMMIT`, only on a branch nobody else uses |
| The whole approach is wrong | Delete the branch | `git switch main && git branch -D agent/login-refactor` |
| The agent ran a migration or changed a service | Neither | Run the down migration or reset the local database; see the failure modes below |

`PATH` is a file path and `COMMIT` is a commit hash from `git log --oneline`. If you reset too far, `git reflog` lists every position the branch has had, and `git reset --hard HEAD@{1}` moves it back.

**Copy-paste prompt: redo one part after a restore**

```text
I restored the checkpoint from before your last change. The new session helper in
src/auth/session.ts was right, and I have committed it. Your changes to
src/db/pool.ts were out of scope, so I discarded them.

Redo only the call sites in src/auth/login.ts and src/auth/refresh.ts so they use
the committed helper. Do not edit src/db/ or src/auth/session.ts. When done, run
`npm test -- tests/auth` and list the files you changed.
```

## Try two approaches with branches and worktrees

When you are not sure which design is right, give each attempt its own branch instead of undoing back and forth in one chat.

Name experiment branches **beside** the feature branch, not under it. Git stores `feature/payments` as a file, so it cannot also create a directory of that name. `git switch -c feature/payments/stripe` fails with `fatal: cannot lock ref ... 'refs/heads/feature/payments' exists`. Use a separate prefix:

```bash
git switch -c feature/payments              # the branch you will merge
git switch -c try/payments-queue            # attempt A, from feature/payments
# ...agent works, gates pass, you commit...
git switch feature/payments
git switch -c try/payments-outbox           # attempt B, from the same starting point
# ...compare, then keep the winner:
git switch feature/payments
git merge --squash try/payments-outbox && git commit -m "payments: outbox-based retries"
git branch -D try/payments-queue try/payments-outbox
```

`git branch -D` is needed after a squash merge, because Git does not see the squashed branch as merged.

To run attempt B while attempt A is still open, create it in a Git worktree instead of with `git switch -c`. A worktree is a second checkout in its own directory; open that directory in a second Cursor window:

```bash
git worktree add -b try/payments-outbox ../myapp-outbox feature/payments
# open ../myapp-outbox in a new Cursor window and run the agent there
git worktree remove ../myapp-outbox        # when you are done with it
```

A new worktree has no installed dependencies and no untracked files such as `.env.local`. Run your setup script there before the agent starts. Cursor can also create worktrees for its agents; running several agents at once, and comparing the same task across attempts, is covered in [the Agents Window and Projects in Cursor](/en/cursor-ide/build/agents-window/).

Judge the attempts on the same evidence: the same tests, the scope check and the size of the diff (`git diff --stat feature/payments..try/payments-outbox`). Prefer the smaller diff when both pass.

## When Cursor checkpoints and branches let you down

**The restore option is gone.** You started a new chat, or the chat that held the checkpoint is no longer available. Recovery: `git status` and `git diff` show exactly what changed since your last commit. Discard it with `git restore .` for tracked files and `git clean -n` (dry run first, then `git clean -f`) for new files. This is why step 5 of the routine commits every green turn.

**A restore wiped your own edits.** You changed a file by hand after the agent's turn, then restored the checkpoint from before that turn. Recovery: if you committed or stashed first, `git stash pop` or `git checkout COMMIT -- PATH` brings the edit back. Otherwise retype it, and from now on commit your manual edits before any restore.

**The code is back but the database is not.** The agent ran a migration or a seed script, and restoring files left the schema in the new state. Recovery: run the migration tool's down or rollback command, or recreate the local database from scratch. Prevention: keep one disposable local database per branch, and require approval for commands with side effects; see [permissions and sandboxing for agents](/en/workflows/harness/permissions-and-sandboxing/).

**`fatal: cannot lock ref` when creating a branch.** A branch named `feature/payments` blocks any branch named `feature/payments/...`. Recovery: use a sibling prefix such as `try/payments-queue`.

**`fatal: 'try/payments-outbox' is already used by worktree`.** Git allows each branch to be checked out in only one worktree. Recovery: open the existing worktree instead (`git worktree list` shows where it is), or check out a detached copy with `git worktree add --detach ../verify try/payments-outbox`.

**The agent keeps editing files you already committed.** Long chats carry earlier plans forward, so the agent "improves" work you have accepted. Recovery: start a new chat per task, restate the scope, and add "Do not modify files that `git diff HEAD` does not list unless I ask" to the prompt. The scope check catches what slips through.

**A reset went too far.** Recovery: `git reflog` shows the commit you were on before the reset; `git reset --hard HEAD@{1}` returns to it.

## How other tools handle the same undo

This page stays on Cursor. Claude Code has its own checkpoints, reached with `/rewind`, or <kbd>Esc</kbd> twice when the prompt input is empty; see [error recovery in Claude Code](/en/claude-code/quick-start/error-recovery/). Codex relies on Git (the `undo` feature is listed as `removed` in `codex features list`, codex-cli 0.157.1), and `codex --worktree` runs a session in a new managed Git worktree (codex-cli 0.157.1); see [worktrees in Codex](/en/codex/automate/worktrees/). The branch routine and the three checks on this page work unchanged with both.

## Where to go next with checkpoints and branching

- Prerequisite: [Agent chat in Cursor](/en/cursor-ide/build/agent-chat/) covers how to prompt the agent and review what it proposes.
- Next: [the Agents Window and Projects in Cursor](/en/cursor-ide/build/agents-window/) scales the branch-per-task routine to several agents at once.
- [Large codebase strategies in Cursor](/en/cursor-ide/build/large-codebase-strategies/) shows how to keep scope tight when the agent cannot see the whole repository.
- [The evidence bundle](/en/workflows/ship/evidence-bundle/) turns the scope check and gate results into what the pull request has to prove.
