两个 AI agent 改了同一个文件:干净合并

Cursor 和 Claude Code 把同一个文件互相覆盖了。停掉两边、各自找回意图、按 hunk 合并,再用 worktree 隔离 agent,杜绝复发。

你在 Cursor 里让 agent 改 Header.tsx,与此同时另一个 terminal 跑着 Claude Code 也在改同一个文件;或者你开了一个 Cursor 后台 agent + 主聊天同时跑;或者团队两个人各自启了 agent 改同一段代码。结果文件被互相覆盖、git status 里出现一堆既不是你也不是任何一个 agent 完整意图的”半成品”。Cursor、Claude Code、Aider 之间没有任何文件锁,它们各自基于读到的那一刻的内容写回,后写的覆盖先写的。

最快修复: 立刻停掉所有 agent(别再继续编),用 git stash 备份一份,把每个 agent 的版本各自抽到独立临时文件里,挑一个当主版本,再手动把另一侧有用的 hunk 叠上去。之后想要不再复发,就给每个 agent 各自一个 git worktree,Cursor 3 和 Claude Code 都能自动创建(Cursor Agents Window 里的 /worktree 命令,或 CLI 里的 claude -w)。完整流程见下文。

先判断你属于哪一类

动手之前先把症状对上原因。这一步决定了你面对的是真正的 git 冲突(带 <<<<<<< 标记),还是无声覆盖(没有标记,只是内容错了)。

你看到的症状最可能的原因跳到
文件里两套命名/注释风格混在一起,但没有冲突标记两个 agent 写了同一文件,后写覆盖先写原因 1
Cursor 提示 “patch could not be applied cleanly” / “file modified externally”后台 agent 的 patch 基于旧版本生成原因 2
git merge 出现 <<<<<<< 标记,两侧都像机器写的两个人、两个分支、两个 agent原因 3
你没动过的文件,IDE 弹出”是否从磁盘重载?“Auto-apply / watch 静默写入原因 4
没有标记,但重构方向自相矛盾(switch 改 ternary 又改回)不同模型把代码拉向相反方向原因 5

常见原因

按出现频率从高到低排序。

1. 两个 agent 在同一 workspace 同时跑

最经典:Cursor 的 Chat / Composer + Claude Code CLI 跑在同一目录。两边都是”读 → 想 → 写”,没人通知对方。

如何判断git status 显示某个文件既不是你刚改的、也不完全是任何一个 agent 输出过的;或者文件里出现两套不同风格的命名 / 注释混在一起。

2. Cursor 后台 agent + 前台 chat 同步运行

一个 Cursor agent 自己跑任务时,你又在前台改同一文件。agent 跑完时 disk 状态已变,它的 patch 基于旧版本,apply 出来就是错位 hunk。截至 2026 年 6 月,Cursor 3(2026 年 4 月 2 日发布)从 Agents Window 启动 agent 时,默认会把每个 agent 放进隔离的 git worktree 里,这能绕开该问题;但只要 agent 和你自己的编辑器共用同一个 checkout(经典的单工作树场景,或者旧版 Cursor),它仍会咬你一口。

如何判断:Cursor 提示 “patch could not be applied cleanly” 或 “file has been modified externally”。

3. 多人在同一仓库各跑各的 agent

两个开发者各自分支但都用 agent 改同一段代码,merge 时冲突看起来像两个”AI 风格”在打架。

如何判断git merge 冲突标记里,两侧都不像人写的,且都”看似合理但实现路径完全不同”。

4. Watch 模式 / autopilot 触发的隐性写入

某些 agent 会在你没主动确认时就改盘:Cursor 的 Auto-Apply,或者 Aider,它默认对每次编辑都自动 commit(自动提交是默认开启的,除非你加上 --no-auto-commits)。你正在改 Header.tsx,agent 后台也改了,IDE 提示”文件已被外部修改是否重载?”。

如何判断:IDE 出现”reload from disk”提示,而你没手动改过那个文件。

5. 不同模型的”风格冲突”

GPT 偏函数式、Claude 偏更分块的命令式,让两个 agent 改同一个模块时,重构方向可能南辕北辙。即使没硬冲突,merge 后代码也是”两套思路缝合体”。

如何判断git log -p 看两个 commit,发现一边把 if/else 改成 switch,另一边把同一 switch 改回 ternary。

最短修复路径

Step 1:立刻停手

所有正在跑的 agent 全部停掉——退出 Cursor(或至少关闭 Composer / Background Agent)、终止 Claude Code CLI、杀掉任何 autopilot / watch 进程。继续编只会让 race condition 越叠越多。

# 找出所有可疑 agent 进程
ps aux | grep -E '(cursor|claude|aider|cline)' | grep -v grep

# 必要时杀掉
pkill -f "claude-code"
pkill -f "aider"

确认 disk 状态稳定 30 秒不变后再进行下一步。

Step 2:备份当前一切,再看 git 状态

防止接下来的合并操作再次丢东西:

git stash push -u -m "multi-agent-mess-$(date +%s)"
# 或者
git branch backup/conflict-snapshot-$(date +%s)
git stash pop  # 恢复改动到工作树

然后看清谁动了什么:

git status
git diff
git log --all --oneline -10

Step 3:分别还原两个 agent 的”意图版本”

把两侧的改动各自独立成一份完整文件,方便对比:

# 假设 Cursor 改完已经 commit
git show <cursor-commit-sha>:src/Header.tsx > /tmp/cursor-version.tsx

# Claude Code 的改动还在工作区
cp src/Header.tsx /tmp/claude-version.tsx

# 都拿到原始版(agent 改动之前)
git show HEAD~2:src/Header.tsx > /tmp/original.tsx

用 IDE 的 3-way diff 同时打开 original.tsxcursor-version.tsxclaude-version.tsx

code --diff /tmp/cursor-version.tsx /tmp/claude-version.tsx
# 或
diff3 /tmp/cursor-version.tsx /tmp/original.tsx /tmp/claude-version.tsx

Step 4:选一个主版本,hunk-by-hunk 合并另一侧

选定主版本(通常是改动意图更明确、覆盖范围更大的那一侧)作为基础,然后逐 hunk 把另一侧的改动叠加上来:

# 把主版本写回工作树
cp /tmp/cursor-version.tsx src/Header.tsx

# 用 add -p 交互式选择 Claude 的 hunk
git diff --no-index src/Header.tsx /tmp/claude-version.tsx | less
# 手动复制有用的 hunk 到 src/Header.tsx

或者更直观,用 Cursor / VS Code 的 “Source Control” 面板,逐 hunk 点击 ”+” stage 你想要的部分,跳过冲突遗留物。两侧都改了同一行时,主版本说了算——不要试图融合,融合通常会引入两边都不曾测试过的新行为。

Step 5:用清晰的 commit message 收尾

git add src/Header.tsx
git commit -m "merge: resolve cursor+claude divergence on Header.tsx

- kept cursor's restructuring of nav menu
- ported claude's a11y label fixes (lines 42-58)
- discarded both agents' duplicate handleClick implementations
- next: single agent per overlap file"

明确写下”留了什么、丢了什么、为什么”,下次出问题能 git log 直接看明白。

怎么确认真的修好了

别只靠肉眼判断合并对不对。继续之前先跑这几条:

# 1. 确认任何地方都没残留冲突标记
git grep -nE '^(<<<<<<<|=======|>>>>>>>)' && echo "MARKERS STILL PRESENT" || echo "clean"

# 2. 文件仍能解析 / 类型检查 / 构建通过
npm run typecheck   # 或:tsc --noEmit、eslint src/Header.tsx

# 3. 两个 agent 各自在意的行为仍然正常
npm test -- Header

如果第 1 步打印 clean、第 2 步通过、相关测试也是绿的,合并就站得住。如果之前能过的测试现在挂了,说明你漏掉了某一侧需要的 hunk,回到 /tmp/cursor-version.tsx/tmp/claude-version.tsx 里把它找出来。

Step 6:建立”重叠文件单一 agent”约定

未来同一文件只让一个 agent 动。最简单做法:

# 工具 A:你做主,前台 Cursor 改 src/components/
# 工具 B:Claude Code 跑后台任务,只动 scripts/、tests/、docs/

# 在 CLAUDE.md 写明
echo "Owned by Cursor: src/components/, src/pages/" >> CLAUDE.md
echo "Owned by Claude Code: scripts/, tests/, docs/" >> CLAUDE.md

或者在 .cursorignore 里临时屏蔽对方负责的目录,让 Cursor 看不到。注意区分:.cursorignore 会让 agent 完全无法读取编辑这些路径,而 .cursorindexingignore 只是把它们排除在搜索索引外(你 @-mention 时 agent 仍能打开)。要做到”这个 agent 不许碰这些文件”,用 .cursorignore

预防:隔离 agent,而不是事后约束

真正的解法是结构性的:从根上不让两个 agent 共用同一个 checkout。截至 2026 年 6 月,两大工具都已经替你把隔离做好了,所以你很少再需要那套手动划分所有权的土办法。

给每个 agent 各自一个 git worktree(最稳的解法)

worktree 是同一个仓库在另一个文件夹、另一个分支上的第二份 checkout,共用同一个 .git object store。两个 agent 待在两个 worktree 里,物理上就不可能互相覆盖文件。

# 手动方式,任何工具都通用
git worktree add ../proj-claude -b feature-claude
git worktree add ../proj-cursor -b feature-cursor
# 让每个 agent 指向各自的文件夹;两边都做完后再 merge 分支
git worktree remove ../proj-claude   # 用完清理
  • Cursor 3(Agents Window):从 Agents Window 启动 agent,每个会自动拿到一个隔离的 worktree;或在 Editor Window 里输入 /worktree,把当前任务跑进一个 worktree。机器级上限由 cursor.worktreeMaxCount 控制(默认 25)。最多可同时并行 8 个 agent。
  • Claude Code:用 claude -w 让会话跑在自己的 worktree 里;或者直接告诉 Claude “use worktrees for your agents”,让 subagent 保持隔离(在自定义 subagent 的 frontmatter 里写 isolation: worktree 可永久开启)。把 .claude/worktrees/ 加进 .gitignore,这份元数据是每台机器本地的,不该共享。

多数人觉得 2 到 4 个 agent 并行还算可控;同一台笔记本超过 4 到 5 个之后,review 和 merge 输出的成本就盖过并行省下的时间了。

用不了 worktree 时

  • 同一个 checkout 同一时间只跑一个编码 agent。若两个非共用一棵树不可,就各自划一块明确、不重叠的目录归它管。
  • CLAUDE.md / .cursorrules / AGENTS.md 里写明”目录所有权”,比如 src/api/ 由 Cursor 负责、scripts/ 由 Claude Code 负责。
  • agent 一做完任务立刻 commit,再切下一个 agent。commit 既是交接信号,也是回滚点。
  • 关掉静默写入:禁用 Cursor 的 Auto-Apply,用 --no-auto-commits 跑 Aider,让每次编辑都需要你确认。
  • 团队多人开发时,PR 描述里标注哪些文件用了哪个 agent,便于 review 和后续 merge 决策。

常见问题

这到底是真的 git 冲突,还是别的? 通常不是。只有原因 3(两个分支 merge)会产生 <<<<<<< 标记。原因 1、2、4、5 都是无声覆盖:文件就是错了,没有任何标记,因为一个 agent 的写入直接替掉了另一个。所以第一件事就是先用 git grep 看有没有标记。

能不能干脆让 AI 帮我解决冲突? 如果是带标记的真冲突,agent 往往能解,但要逐行 review,因为它很乐意选一边、顺手丢掉另一边需要的行为。如果是无声覆盖(没标记),其实没有什么可”解决”的,你必须像 Step 2 到 4 那样自己把两侧的意图版本找回来。

只要我小心点,两个 agent 能安全改同一个文件吗? 没有可靠办法。Cursor、Claude Code、Aider 之间没有文件锁,最终永远是后写覆盖先写。要么给它们分开文件/目录,要么分开 worktree。如果某个文件确实两边都要改,就让 agent 一前一后跑,中间 commit 一次。

Cursor 3 自动用 worktree 了,为什么我还会冲突? worktree 隔离只对你启动进 worktree 的 agent 生效(Agents Window,或 /worktree)。如果你在自己的主 checkout 里编辑同一文件、而 agent 也在那棵树上跑,或者你用的是旧版 Cursor,就又退回到共用一棵树、后写覆盖先写。

合并过程中怎么不丢东西? 动手前先快照:git stash push -u -m backupgit branch backup/conflict-snapshot-$(date +%s)。然后在改工作区副本之前,把每个 agent 的完整文件抽到 /tmp。有了快照加临时文件,整个合并过程怎么走都不会永久丢数据。

相关阅读

标签: #AI 编程 #排查 #排查