Checkpoints and branching: undo Cursor agent changes safely
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
Section titled “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 resetor 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 referror the classic “micro-branch” advice runs into.
What Cursor documents, and what this page relies on
Section titled “What Cursor documents, and what this page relies on”Cursor’s documentation site could not be re-checked on 26 September 2026, so 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 | Contents not re-verified as of 26 September 2026 | 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?
Section titled “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:
- Create a throwaway branch:
git switch -c scratch/checkpoint-test. - Ask the agent: “Add a comment line to
README.md, createtmp-checkpoint-test.txtcontaininghello, then runecho ran >> tmp-command-log.txtin the terminal.” - Edit
README.mdby hand and add a second line of your own. - Restore the checkpoint from before that agent turn in the chat.
- Run
git status --shortandcat README.md. Note which of the three files came back, disappeared or kept their changes, and whether your manual line survived. - Clean up:
git switch -andgit 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
Section titled “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.
-
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. -
Branch per task. Run
git switch -c agent/login-refactor. One task per branch means that deleting the branch is always a complete undo. -
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 explains when Plan mode is the better start.
-
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.
-
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.
Keep or discard an agent turn without reading every line
Section titled “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:
{ 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:
git stash push -u -- src/auth/ # set aside the implementation, keep tests/auth/npm test -- tests/auth # the new test must FAIL heregit stash pop # bring the implementation backnpm test -- tests/auth # and PASS hereCheck 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 and the test-first loop in Cursor.
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. 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.
Which undo do you need?
Section titled “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.
Try two approaches with branches and worktrees
Section titled “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:
git switch -c feature/payments # the branch you will mergegit switch -c try/payments-queue # attempt A, from feature/payments# ...agent works, gates pass, you commit...git switch feature/paymentsgit switch -c try/payments-outbox # attempt B, from the same starting point# ...compare, then keep the winner:git switch feature/paymentsgit merge --squash try/payments-outbox && git commit -m "payments: outbox-based retries"git branch -D try/payments-queue try/payments-outboxgit 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:
git worktree add -b try/payments-outbox ../myapp-outbox feature/payments# open ../myapp-outbox in a new Cursor window and run the agent theregit worktree remove ../myapp-outbox # when you are done with itA 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.
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
Section titled “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.
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
Section titled “How other tools handle the same undo”This page stays on Cursor. Claude Code has its own checkpoints, reached with /rewind, or Esc twice when the prompt input is empty; see error recovery in Claude Code. 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. The branch routine and the three checks on this page work unchanged with both.
Where to go next with checkpoints and branching
Section titled “Where to go next with checkpoints and branching”- Prerequisite: Agent chat in Cursor covers how to prompt the agent and review what it proposes.
- Next: the Agents Window and Projects in Cursor scales the branch-per-task routine to several agents at once.
- Large codebase strategies in Cursor shows how to keep scope tight when the agent cannot see the whole repository.
- The evidence bundle turns the scope check and gate results into what the pull request has to prove.