rebase 完了、或者刚切了分支、或者刚 pull 了一个大 merge,问 Composer「parseConfig helper 在哪用过?」Cursor 信心十足地指向 src/utils/oldHelpers.ts——一个早就不存在的文件。或者引用的行号来自一个已经被大改的文件。它提的 diff 落在错位的行段上。codebase 索引是 Cursor 为了快速回答 @codebase 而建的缓存,靠文件系统事件追变化,但 rebase、分支切换、大 merge 这种批量改动它经常追不上。索引落后于现实,模型拿到的是陈旧上下文,于是回得自信而错。
常见原因
按命中率从高到低。
1. Watcher 漏掉了批量文件变更
索引器靠文件系统事件。rebase 在很短的时间里改了很多文件;尤其在 macOS 上,fsevents 会合并事件,索引器看到的变更比实际少。
怎么判断:设置 → Codebase → 看「Last indexed」时间戳。早于你 rebase 的时间——就是漏了。
2. 切分支后还在用同一份 workspace 存储
workspace 存储按文件夹路径分桶,不按分支。git checkout feature-x 之后索引还停在上个分支的 chunk 上,要等它自己追。
怎么判断:问 Composer 一个你确定在切分支时改了名的文件。它还引用旧名——索引就停在旧分支上。
3. 索引被暂停或限流了
Cursor 检测到高 CPU 或低电量时会暂停后台索引。你 rebase 完之后那次 re-index 根本没跑。
怎么判断:设置 → Codebase → status。显示 Paused 或 Throttled,并带电量/CPU 提示——就是没跑。
4. .gitignore 或 .cursorignore 把新文件排除了
rebase 之后新树里有些文件落在你现在 ignore 规则会排除的目录里。索引器跳过,Composer 自然看不到。
怎么判断:检查新 HEAD 下的 .cursorignore 和 .gitignore。新文件路径匹中某条 pattern——就是它。
5. embeddings 缓存按旧内容 hash 存的还在
Cursor 按 file path + content hash 缓存 embeddings。rebase 后 hash 变了、path 没变;旧 vector 还在被慢慢淘汰,查询结果命中陈旧 neighbor。
怎么判断:下面 Step 3 强制 re-index 后立刻好转——就是它。
6. 你在用 worktree,Cursor 开的是错的那个
用 git worktree 时,Cursor 开在主仓路径、但你在 worktree 路径里编辑——索引就指向主 worktree。
怎么判断:看标题栏路径,和你终端里 pwd 不一致——你已经走偏了。
开始前
- 把正在改的内容提交或 stash;re-index 从磁盘读,不要让它把半成品缓存进去。
- 给 CPU 留至少一两分钟预算;大仓全量重建索引不是瞬间的事。
- 知道自己代码库规模(文件数、总 LOC),心理预期到位。
需要收集的信息
- Cursor 版本(Help → About)。
- 仓库规模:
find . -type f -name '*.ts' -o -name '*.js' -o -name '*.py' | wc -l。 - 设置 → Codebase → 「Last indexed」时间戳。
git log --oneline -10确认 rebase 发生过。- 是不是在用 worktree(
git worktree list)。 .cursorignore和.gitignore内容。~/.cursor/logs/里最近匹配index或embed的条目。
一步一步修复
Step 1:确认 Cursor 看到的 HEAD 是对的
Cursor 集成终端跑 git rev-parse HEAD,再用外部终端在同一路径跑一遍,要一样。不一样——Cursor 开的是别的路径(worktree、symlink 副本)。
Step 2:reload 窗口
Cmd+Shift+P → 「Developer: Reload Window」。重新扫一遍工作树。三成左右的过期索引这一步就够,一两分钟内 indexer 能追上。
Step 3:强制全量 re-index
设置 → Codebase → 点「Re-index」。把现有 chunk 和 embedding 缓存丢掉、从磁盘重建。10 万文件的仓大概 5-15 分钟。
Step 4:核对 ignore 规则
rebase 后多了新目录的话,确认 .cursorignore 没排除它。Cursor 同时遵守 .cursorignore 和 .gitignore。常见坑:pattern dist/ 会把任何 package 下的 dist/ 都排掉。
Step 5:在 Composer 里清掉上下文
Composer 里输 /clear 或者新开一个对话。已有线程会继续用它先前抓到的文件引用;新线程才会重新抓。
Step 6:worktree 直接开 worktree 路径
退出 Cursor,用 cursor /path/to/the/worktree 或 File → Open Folder 打开。每个 worktree 一个独立 Cursor 窗口,各自独立索引。
Step 7:还不对,把 workspace 存储清掉
兜底:退出 Cursor,删 ~/Library/Application Support/Cursor/User/workspaceStorage/<hash>/(在意聊天记录的话先备份)。重开项目,Cursor 会从零建一份干净 workspace 存储和索引。
怎么验证修好了
- 问 Composer 一个你在 rebase 里改名了的文件路径,它必须回新路径。
- 问一个挪了位置的函数的行号,要和编辑器里看到的一致。
- 用
@codebase跑一个应该命中新加文件的 query,应该出现在引用里。 - 在挪过位置的文件上 Cmd-K 改一下,diff 要落在正确行段。
长期预防
- 任何 rebase 或大 merge 之后点一下「Re-index」。当成 build step 看待。
- 大 diff 的分支切换,别开着 Cursor 切——先退出、切完、再启。
- 一个 worktree 一个 Cursor 窗口,不要跨 worktree 共用索引。
.cursorignore写得简洁、明确;过宽的 pattern 会悄悄缩小索引范围。- Cursor 钉在一个已知索引稳定的版本上——新版本不一定更快。
容易踩的坑
- rebase 后立刻信
@codebase的回答。头一分钟基本都是 stale。 - 在电池模式下 re-index,会被 throttle,看起来跑完了实际没。
- index 跑着改
.cursorignore,runner 可能还在用老规则,重启才生效。 - 以为「Reload Window」会强制 re-index——不会,只是重扫已有 chunk。
- 让 Cursor 索引 node_modules 之类。vendor 和 build 目录永远 ignore 掉。
常见问答
- 索引能撑多大? 超大 monorepo(百万级文件)Cursor 比较吃力,考虑用
.cursorignore收缩范围或拆 workspace。 - 索引会上传数据吗? 默认 embedding 在服务端算。设置 → Privacy → 「Privacy mode」全本地,但更慢。
- 为什么每次 pull 完 CPU 飙高? watcher 触发了增量 re-index。正常的,比全量快。
- 能从命令行触发 re-index 吗? 没有官方方式。走设置 → Codebase → 「Re-index」。
- 切分支时 Cursor 会自动 re-index 吗? 部分会。文件增删它能跟,但大幅原地改写经常漏。大 rebase 后手动点 Re-index 才稳。