让 AI 帮你解 git 冲突:哪些能交出去,哪些必须人来判

2026-07-28

数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。

大多数人觉得「AI 解 git 冲突不靠谱」,归因归错了:问题不是模型读不懂 <<<<<<<>>>>>>> 之间那几十行,而是解冲突真正需要的信息压根不在冲突标记里。 冲突块只告诉你两侧文本长什么样,不告诉你两侧各自想干什么、哪一侧的行为才是当前要保留的。你把一段缺了一半前提的材料丢给任何人——模型或新同事——他都只能猜一个「看起来能编译」的结果。而 git 合并最阴的地方就在于:解成单边、丢掉一侧改动,代码照样编译通过,测试也可能照样绿,直到线上出现一个谁都复现不了的行为。

所以这篇不讲「怎么让 AI 更聪明地解冲突」,讲的是一条可执行的处置顺序:先按现象分因,判断这个冲突属于哪一类;再决定动作是交给 AI 还是人来拍板;最后不管谁解的,都过同一套三步验证。站内另外两篇是它的上下游——Claude Code 配 git worktree 并行多开 讲的是怎么让并行任务少撞车(冲突发生之前),AI 改坏代码怎么回滚 讲的是改错了怎么退回去(冲突解错之后),本篇只管冲突已经摆在你面前的这一刻。

一、动手之前先把现场看清楚

很多人一看到冲突就直接打开编辑器开始删标记,这是顺序错了。冲突文件的数量和类型决定了后面所有动作,先花两分钟收集四样东西。

第一是冲突清单和状态码:

git status --short
git diff --name-only --diff-filter=U

git status --short 左侧的两位字母是有信息量的:UU 是两边都改了同一文件,AA 是两边各自新增了同名文件,DU / UD 是一边删了一边改了。后两类的处置逻辑跟普通文本冲突完全不同,看到就要停一下——一边删文件一边改文件,通常意味着有人做了重构或移动,光看冲突块看不出来。

第二是合并基点。冲突大小失控时,先怀疑基点:

git merge-base HEAD MERGE_HEAD
git log --oneline --left-right HEAD...MERGE_HEAD

--left-right 会用 <> 标出提交属于哪一侧。如果某一侧突然冒出几十个你从没见过的提交,或者双方有大量内容相同但哈希不同的提交,那大概率是有人 rebase 过或者强推过共享分支,这时候解冲突是治标,先把分支拓扑理顺才是正事。

第三是两侧的意图。这一步是整篇的关键,也是最容易被跳过的:

git log --merge -p -- path/to/file

--merge 只列出与冲突文件相关、且不同时存在于两侧的提交,配上 -p 就能看到每一侧究竟为什么改这个文件。这些提交信息(以及它们关联的 issue 编号)才是判断依据。

第四是把冲突块换成三方视图:

git config merge.conflictStyle diff3
git checkout --conflict=diff3 -- path/to/file

第一条改的是配置,但它不会追溯改写工作区里已经写好的冲突标记,所以要用第二条把那个文件的冲突重新生成一次——注意这会丢掉你在这个文件里已经做的手工编辑,所以务必在动手删标记之前先执行。默认的两方冲突标记只给「我方」和「对方」,diff3 会在中间多显示一段共同祖先的原始内容(以七个 | 开头的那段)。较新的 git 还支持 zdiff3,会把两侧相同的部分压缩掉,块更小更好读。有了祖先内容,「谁改动了什么」才是可推断的;没有它,你只能对着两段陌生文本二选一。

二、七类现象的判别表

按下面这张表先归类,再决定动作。同一次合并里不同文件可能属于不同类别,分开处置,别一把梭。

现象大概率成因怎么验证处置动作
冲突块两侧内容几乎一样,只有缩进、引号、行尾不同格式化配置或行尾符不一致git diff --ignore-all-space :2:<file> :3:<file> 输出为空就说明只是空白差异(:2: 是我方、:3: 是对方;冲突态下不带路径直接 git diff 出来的是带冲突标记的合并视图,永远不为空,不能用来判断);再查 core.autocrlf 与仓库里的格式化配置可以交给 AI,也可以直接取一侧再整体跑一遍格式化;根因是把格式化规则固定进仓库
冲突集中在依赖锁文件、生成代码、快照文件两侧各自重新生成过产物看该文件是否由某条生成命令产出(构建脚本、代码生成器、测试快照更新)不手改也不让 AI 改:取一侧,然后用生成命令重新产出
同一个文件两边改了不同的函数,语义互不相干正常的并行开发git log --merge -p 看两侧提交主题是否互不相关适合交给 AI,但必须把两侧提交信息一起给它
同一处逻辑两边改成了不同行为(阈值、判断条件、返回结构)需求层面撞车找到两侧提交对应的 issue 或 PR;找不到就去问人人来判。AI 只能帮你把差异列清楚,不能替你定行为
冲突文件里出现整段陌生代码或大范围删除分支被 rebase 或强推过,合并基点已经不对git merge-base--left-right 核对提交范围停手,先修分支拓扑,不要在错误基点上解冲突
解完能编译,但测试挂在看似无关的模块解成了单边,丢掉了一侧的改动提交合并后用 git diff <merge>^1 <merge> 看对方分支(第二父)带来的改动是否都在结果里git checkout -m -- <file> 把冲突原样恢复,重解这个文件
数据库迁移、schema、路由注册表等「顺序敏感」文件冲突两侧各自追加了条目,序号或顺序撞车看目录里的序号、时间戳、注册顺序是否重复人来重排。必要时重命名条目,并在本地跑一次正向与回退

这张表的用法是自上而下:越靠上越机械、越适合交出去;越靠下越依赖只存在于人脑子里的上下文。

三、分工:什么交给 AI,什么必须人拍板

能交出去的几类,以及怎么交

能交出去的冲突有一个共同特征:判断依据全部在你能提供的材料里。格式差异、互不相干的并列改动、导入语句和依赖声明的合并、同一个测试文件里两边各加了不同用例——这几类的正确答案是可以从文本本身推出来的,模型做得又快又稳,比你手动删标记还少手误。

交的时候材料要给全,至少三样:diff3 风格的冲突块(带共同祖先)、两侧相关提交的信息、以及这个文件在项目里的角色约定。只把冲突块贴过去,就等于让它猜。上下文怎么组织更省事,可以看 Claude Code 的上下文管理

还有一条纪律:让它解,但不让它提交。工具直接执行 git commitgit merge --continue,你就失去了最便宜的检查窗口。把合并动作留在自己手里,是这类任务里性价比最高的人工卡点,思路和 Agent 的人工介入设计 是一致的。

顺带说清一件事:Claude Code、Copilot 这类海外工具,官方对中国大陆有区域限制、不支持直连,账号与结算也各有各的规则;市面上存在第三方中转,但它意味着你的代码要多经过一层不受你控制的链路——解冲突时贴过去的往往是核心业务文件,这个风险自己掂量,本文不做推荐、也不给渠道。

必须人来判的四类

反过来,下面四类不管模型多强都别交出去,因为缺的信息不在代码里。

行为撞车。 两侧把同一个条件、同一个默认值、同一个返回结构改成了不同样子。这不是合并问题,是两个需求打起来了。正确动作是找到两侧的负责人对齐,哪怕只是一条消息确认「以你这版为准」。让模型选一个,它会给你一个语法正确、逻辑上谁都不满意的折中版本。

安全与权限相关代码。 鉴权判断、越权校验、密钥读取路径、SQL 拼接位置。这些地方一侧的改动往往是在补一个漏洞,另一侧只是顺手重构。合并时把补丁那一侧「合掉」,漏洞就悄悄回来了,而测试通常不会覆盖它。这类文件建议在冲突清单里单独标出来,人工逐块看。

数据结构与迁移。 迁移脚本、序列化格式、对外接口字段。它们的约束是「已经跑过的不能改、顺序不能乱」,这个约束存在于生产环境的状态里,不在仓库里。

大重构与功能改动撞在一起。 一侧移动了文件、改了目录结构或重命名了核心接口,另一侧在旧位置上加了功能。这时候冲突块的信息量最低(git 看到的是删一片加一片),人必须先决定「以哪一侧的结构为基准」,再把另一侧的改动按新结构重写一遍。让 AI 参与的正确姿势是:结构由你定好之后,请它按新结构搬运具体实现。

四、解完必须做的三步验证

不管冲突是你解的还是 AI 解的,这三步都跑,顺序不能换。第一步防手误,第二步防丢改动,第三步防行为回退。

第一步:结构验证,确认没有残留、能构建。

grep -rnE '^([<>|]{7}|={7})( |$)' -- . || echo "no conflict markers"
git diff --check

这条 grep 把三种标记连同 diff3 多出来的祖先分隔线(七个 |)一起扫,漏掉后者是很常见的翻车方式——上下两半都对,中间夹着一段祖先版本的旧代码。git diff --check 也会对残留标记报 leftover conflict marker,但显式 grep 一遍更保险,尤其是那些还没进索引、不出现在 diff 里的文件。扫干净之后再跑一次完整构建和类型检查。这一步只证明「文件没被解坏」,不证明任何语义正确。

第二步:双亲 diff,确认两侧意图都还在。

这是最容易被跳过、也最值钱的一步。合并提交有两个父,正确的合并结果应该同时包含两侧的意图:

git diff HEAD^1 HEAD --stat
git diff HEAD^2 HEAD --stat
git show --cc HEAD

看的重点不是行数对不对,而是:相对第一父,你应该看到对方分支带来的全部改动;相对第二父,你应该看到自己分支带来的全部改动。如果某一侧的关键改动在这两个 diff 里都找不到痕迹,说明它在解冲突时被吃掉了。git show --cc 只显示相对两个父都有差异的部分,正常合并这里应该很小;如果它列出一大堆内容,说明你在合并里夹带了大量「既不属于我方也不属于对方」的新写法,得解释清楚为什么。

这一步也是最适合请 AI 帮忙看的:把两个 diff 交给它,让它逐条核对两侧提交信息里承诺的改动是否都在结果里。它做这种「清单对账」比人耐心。配合 AI 代码评审工具 的思路,可以固化成合并后的固定检查动作。

第三步:行为验证,跑冲突涉及的那条路径。

全量测试要跑,但别只跑全量测试。冲突文件往往是并行开发的热点,恰恰是测试覆盖的薄弱区。补两件事:一是把两侧提交各自新增的测试都跑一遍(合并后它们应该同时存在、同时通过),二是手动走一遍冲突涉及的业务路径。接口层的话,一条 curl 就够定性:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/api/health

看到 401 / 403 就查鉴权那一侧是不是被合掉了,500 就去看服务端日志,连不上则先确认服务起没起。这些状态码本身就是很好的分因线索。

五、什么情况下别再折腾

解冲突有一个隐蔽的成本陷阱:它是纯手工活,投入产出比会随时间急剧下降。出现下面任一信号,就该止损而不是继续硬解。

  • 冲突文件超过十几个,且横跨多个模块。 这通常说明分支活得太久。止损动作是 git merge --abortgit rebase --abort 回到干净状态,然后把大合并拆成几次小合并(先合基础设施改动,再合功能改动),或者请对方先把他的分支 rebase 到最新主干、把他自己的冲突先解掉。
  • 同一个冲突你解了两遍以上,每次都因为验证不过而回退。 说明缺的是决策不是操作。停手去问人,一条消息能省两小时。
  • 合并基点明显不对。 先修拓扑。在错误基点上解出来的冲突,往后每次合并都会再冲一遍。
  • 冲突在生成产物上。 一分钟都别花在手改上,取一侧后重新生成。
  • 你已经开始「凭感觉」保留代码。 这是最硬的止损点:一旦你说不清为什么保留这一行,就意味着后面的验证也验不出问题,因为你不知道该期待什么。

止损之后的三条常见退路:把 merge 换成让对方 rebase(把冲突推给更懂那侧改动的人);把一个大 PR 拆成两三个小 PR 分批合;或者直接放弃这次合并,从最新主干重新拉一个分支,把自己的改动按新结构重写一遍——重写往往比硬解快,尤其当你的改动只有几百行。

顺手打开 rerere,让重复冲突只解一次:

git config --global rerere.enabled true

它会记录你对某个冲突块的解法,下次遇到一模一样的冲突自动套用。长期分支反复合主干时,这个开关省下的时间相当可观。

六、避坑清单

--ours / --theirs 时搞反了方向。 为什么会踩:这两个词的含义在 merge 和 rebase 里是反的——rebase 期间「ours」指的是你正在往上重放的那个基底,不是你的功能分支。怎么避:动手前先 git status 看清当前是 merge 还是 rebase,并且只在整文件取舍(比如生成产物)时用它们,逻辑代码不用。

在冲突未解完的状态下切分支或 stash。 为什么会踩:想「先看看主干怎么写的」,一 checkout 就把中间状态搅乱了,冲突信息也没了。怎么避:需要参照就用 git show <branch>:path/to/file 直接读另一侧的文件内容,不切换工作区。真要并行看,用独立的 worktree。

让 AI 顺手「优化」了冲突周边的代码。 为什么会踩:解冲突时上下文都在,模型很容易多改几行;结果这些改动混在合并提交里,评审时没人能分清哪些是合并、哪些是新写的。怎么避:明确要求只处理冲突块内的内容,合并提交里出现的额外改动一律拆成独立提交。第二步的 git show --cc 就是这条的检查手段。

只看编译过没过就提交。 为什么会踩:解成单边最典型的表现就是「编译通过、测试通过、少了一个功能」。怎么避:双亲 diff 不能省。

锁文件手动解冲突。 为什么会踩:锁文件是完整性快照,手拼出来的版本可能自身矛盾,装的时候不报错,运行时才出问题。怎么避:取一侧,跑生成命令重出,然后把整个依赖安装流程跑通一次。

迁移文件按「都保留」处理。 为什么会踩:两侧各加一个迁移,直接都留下看起来最安全,但如果它们改的是同一张表、顺序又反了,执行时就炸。怎么避:人工确定执行顺序,本地跑一次正向和回退。

hooks 或 CI 在合并态被绕过。 为什么会踩:某些提交路径(尤其是工具代跑的合并)会跳过本地检查,问题一路带到主干。怎么避:合并提交也当普通提交对待,push 前手动跑一次仓库约定的检查命令。

默认冲突样式没换成 diff3。 为什么会踩:两方视图丢掉了共同祖先,你和模型都只能靠猜。怎么避:全局设一次 merge.conflictStyle,这是一次性成本、长期收益。

收束

把这篇的判断压成一句:冲突的处置难度取决于「判断依据在不在文本里」,而不是取决于冲突块有多长。 依据在文本里的(格式、并列改动、导入合并、产物文件)尽量交出去省时间;依据在人脑和生产环境里的(行为选择、安全补丁、迁移顺序、结构基准)必须人拍板。中间那条线,靠三步验证兜住。

合并前后过一遍这份自检清单:

  1. 冲突清单和状态码看过了,DU / AA 这类单独处理了吗?
  2. 合并基点核对过了吗?有没有异常的提交范围?
  3. 冲突样式是 diff3 或 zdiff3 吗?两侧提交信息看过了吗?
  4. 这批冲突按类别分开处置了吗,还是一把梭?
  5. 交给 AI 的部分,材料给全了吗?提交动作留在自己手里了吗?
  6. 残留标记扫过了吗?构建和类型检查过了吗?
  7. 相对两个父的 diff 都看过了吗?两侧关键改动都在吗?
  8. 两侧各自的测试都跑了吗?冲突涉及的那条业务路径手动走过吗?
  9. 有没有夹带无关改动?git show --cc 干净吗?
  10. 如果卡了超过预定时间,止损点是什么,你想好了吗?

第 7 条和第 10 条是最常被跳过的两条,也正好是事后最贵的两条。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。