Cursor Codebase Index Stale After Rebase or Branch Switch

After a rebase or branch switch, Cursor references files and line numbers that no longer exist. Indexer fell behind your git state. Force re-index and harden the workflow.

You finish a rebase, switch branches, or pull a big merge, then ask Composer something like “where is the parseConfig helper used?” and Cursor confidently points at src/utils/oldHelpers.ts — a file that no longer exists. Or it cites line numbers from a file that has been heavily rewritten. The diff it proposes targets line ranges that have shifted. The codebase index is the cache Cursor builds to answer @codebase queries fast; it watches your filesystem for changes but it does not always catch the bulk rewrites that come with rebases, branch switches, or large merges. The index lags reality, the model gets stale context, and the answers are wrong with full confidence.

Common causes

Ordered by hit rate, highest first.

1. Watcher missed a bulk filesystem change

The indexer relies on filesystem events. A rebase rewrites many files in a tight loop; on macOS especially, fsevents can coalesce and the indexer sees a smaller change set than reality.

How to judge: Settings → Codebase → check “Last indexed” timestamp. If it predates your rebase, the watcher missed it.

2. Branch switch reused the same workspace storage

Workspace storage is keyed by folder path, not by branch. After git checkout feature-x, the index still holds the previous branch’s chunks until it catches up.

How to judge: Ask Composer about a file you know was renamed in the branch swap. If it references the old name, the index is on the old branch.

3. Indexing is paused or rate-limited

If Cursor detected high CPU or low battery it may have paused background indexing. Re-index never ran after your rebase.

How to judge: Settings → Codebase → status. “Paused” or “Throttled” with a battery/CPU note means re-index has not run.

4. .gitignore or .cursorignore excluded the new files

After a rebase the new tree may have files in directories your ignore rules now exclude. The indexer skips them; Composer cannot see them.

How to judge: Inspect .cursorignore and .gitignore at the new HEAD. If the new file’s path matches a pattern, that is why it is missing.

5. The embeddings cache holds vectors keyed to old file content

Cursor caches embeddings by file path plus content hash. After a rebase the hash changes but the path stays; old vectors are evicted slowly, so queries hit stale neighbors.

How to judge: Forced re-index fixes it (Step 3 below). If results improve right after, this was the cause.

6. You are on a worktree and Cursor opened the wrong one

If you use git worktree, opening Cursor at the main repo path while editing in the worktree path gives you an index pointing at the main worktree.

How to judge: Check the title bar path. If it differs from pwd in the terminal you have been using, you are out of sync.

Before you start

  • Commit or stash any in-flight changes; the re-index reads from disk and you do not want partial state cached.
  • Have at least a minute or two of CPU budget; a full re-index of a large repo is not instant.
  • Know the size of your codebase (file count and total LOC) so you can set expectations.

Information to collect

  • Cursor version (Help → About).
  • Repo size: find . -type f -name '*.ts' -o -name '*.js' -o -name '*.py' | wc -l.
  • Settings → Codebase → “Last indexed” timestamp.
  • Output of git log --oneline -10 to confirm the rebase happened.
  • Whether you are on a worktree (git worktree list).
  • .cursorignore and .gitignore contents.
  • Recent entries in ~/.cursor/logs/ matching index or embed.

Step-by-step fix

Step 1: Confirm Cursor knows the right HEAD

Open the integrated terminal in Cursor and run git rev-parse HEAD. Open an external terminal at the same path and run the same. They must match. If they do not, Cursor opened a different path (worktree, symlinked copy).

Step 2: Reload the window

Cmd+Shift+P → “Developer: Reload Window.” This re-scans the working tree. For ~30% of stale-index cases this is enough; the indexer catches up within a minute or two.

Step 3: Force a full re-index

Settings → Codebase → click “Re-index.” This drops the existing chunk and embedding cache for the workspace and rebuilds from disk. On a 100k-file repo this can take 5-15 minutes.

Step 4: Verify your ignore rules

If new directories appeared after the rebase, make sure .cursorignore does not exclude them. Cursor honors .cursorignore plus .gitignore. A common gotcha: pattern dist/ also excludes nested dist/ under any package.

Step 5: Clear codebase context in Composer

In Composer, type /clear or open a new conversation. Existing threads keep the file references they already gathered; new threads get a fresh fetch.

Step 6: For worktrees, open the worktree path directly

Quit Cursor. Re-open by cursor /path/to/the/worktree or File → Open Folder. Each worktree should be its own Cursor window with its own index.

Step 7: If still wrong, nuke the workspace storage

As a last resort, quit Cursor and remove ~/Library/Application Support/Cursor/User/workspaceStorage/<hash>/ (back it up first if you care about chat history). Re-open the project; Cursor builds a clean workspace storage and a clean index.

Verify

  • Ask Composer for the path of a file you renamed in the rebase. It must return the new path.
  • Ask for the line number of a function that moved. It should match what you see in the editor.
  • Run @codebase on a query that should hit a newly added file. It should appear in the references.
  • Run a Cmd-K edit on a moved file; the diff should target the right lines.

Long-term prevention

  • After any rebase or large merge, click “Re-index” once. Treat it like a build step.
  • Avoid switching branches with Cursor open if the diff is huge; quit, switch, relaunch.
  • Use one Cursor window per worktree, never share an index across them.
  • Keep .cursorignore lean and explicit; broad patterns silently shrink the index.
  • Pin Cursor to a version where indexing is known stable for your repo size — newer is not always faster.

Common pitfalls

  • Trusting @codebase answers right after a rebase without checking. The first minute is almost always stale.
  • Re-indexing on battery power; it can throttle and look done while still incomplete.
  • Editing .cursorignore mid-index; the runner may use the old rules until restart.
  • Assuming “Reload Window” forces re-index. It does not; it just re-scans existing chunks.
  • Letting Cursor index a node_modules-equivalent. Always ignore vendored or build directories.

FAQ

  • How big can the index get? For very large monorepos (1M+ files) Cursor can struggle; consider scoping with .cursorignore or splitting workspaces.
  • Does indexing share data with the cloud? Yes, embeddings are computed server-side by default. Settings → Privacy → “Privacy mode” keeps everything local but slower.
  • Why does my CPU spike after every pull? The watcher kicked off an incremental re-index. Normal; finishes faster than a full re-index.
  • Can I trigger re-index from the CLI? Not officially. Settings → Codebase → “Re-index” is the supported path.
  • Does Cursor re-index on branch switch automatically? Partially. It picks up file deletes and adds but can miss large in-place rewrites. Always click Re-index after a big rebase.

Tags: #Cursor #Indexing #git #Troubleshooting