Skip to content

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 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

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.

FeatureWhat Cursor documentsLast checkedWhat this page assumes
CheckpointsA Checkpoints page in the agent chat docs describes automatic snapshots of the agent’s changesContents not re-verified as of 26 September 2026A 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 2026A worktree is an ordinary Git worktree with its own branch
/rewind in the Cursor CLIListed among the CLI slash commands, without a definition28 August 2026Nothing. 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.

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

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.

  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 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.

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:

Terminal window
{ 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:

Terminal window
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 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.

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

SituationUseCommand or action
The last agent turn is wrong and you have not committed sinceCheckpoint restoreRestore the checkpoint before that turn in the chat
One or two files are out of scope; the rest is goodGit, per filegit restore --source=HEAD -- PATH for tracked files; delete new files
Part of one file is good, part is notGit, per hunkgit restore -p -- PATH and answer y for each hunk to discard
A turn you already committed is wrongGit, new commitgit revert COMMIT keeps history intact
The last few commits on your agent branch are wrongGit, move the branchgit reset --hard COMMIT, only on a branch nobody else uses
The whole approach is wrongDelete the branchgit switch main && git branch -D agent/login-refactor
The agent ran a migration or changed a serviceNeitherRun 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:

Terminal window
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:

Terminal window
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.

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.

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”