:Git 冲突解决与合并策略)
1. 多人协作里冲突到底卡在哪一步只要你参与过三人以上的 Git 协作大概率经历过这种时刻本地改完准备推git pull一执行终端刷出一片CONFLICT (content): Merge conflict in ...然后你盯着文件里那几行、、发呆不知道哪边该留、哪边该删。更麻烦的是冲突往往不止一个文件改完一个还有下一个最后干脆git merge --abort重来。这篇聚焦的就是这个场景多人协作中 Git 合并冲突的定位与解决流程。我会把冲突标记怎么读、手动合并怎么下手、合并工具怎么选、回滚怎么做拆成能直接复制的步骤。同时给出一份.gitconfig冲突处理配置配合分步验证命令让团队在真实分支合并里稳定落地。适合已经会用git add、git commit、git push但一遇到冲突就想重开分支的开发者。需要说明的是冲突本身不是错误它是 Git 在告诉你「这两处改动它不敢替你决定」。理解这一点后面的操作就顺了。2. 前置准备把冲突处理环境配好在动手解决冲突之前先把工具链理顺。这里不涉及任何网络加速类工具全部是 Git 自带能力加一个可视化 diff 工具。2.1 确认 Git 版本与基础信息先确认版本低于 2.30 的建议升级因为merge.conflictStyle的zdiff3模式在较新版本才稳定git --version git config --global user.name 你的名字 git config --global user.email youexample.com2.2 配置冲突显示风格默认的冲突标记只有和看不出共同祖先长什么样。开启zdiff3后会多出一段|||||||显示原始版本判断起来清楚很多git config --global merge.conflictStyle zdiff32.3 配置合并工具与默认策略选一个可视化工具Linux/macOS 常用meldWindows 常用Beyond Compare或 VS Code 内置的合并编辑器。下面以 VS Code 为例git config --global merge.tool vscode git config --global mergetool.vscode.cmd code --wait --merge $REMOTE $LOCAL $BASE $MERGED git config --global mergetool.keepBackup falsekeepBackup false很关键否则每次解决冲突都会留下.orig备份文件容易误提交。2.4 一份可直接复制的 .gitconfig 片段把下面这段追加到~/.gitconfig团队可以统一[merge] conflictStyle zdiff3 tool vscode [mergetool] prompt false keepBackup false [mergetool vscode] cmd code --wait --merge $REMOTE $LOCAL $BASE $MERGED [diff] algorithm histogram [rerere] enabled true autoupdate true其中rererereuse recorded resolution是很多人忽略的功能它会记录你解决过的冲突下次遇到相同冲突自动套用。多人长期协作同一模块时能省下大量重复劳动。注意rerere记录的是「冲突片段到解决结果」的映射如果团队对同一段代码反复产生相同冲突开启它收益明显但不要把它当成免检机制自动套用后仍要跑测试。3. 可复制配置冲突定位与解决全流程配置好之后进入实际操作。假设当前在feature/user-search分支要合并进develop。3.1 合并前预检先知道会不会冲突不要直接git merge然后被一堆冲突淹没。先做一次「试合并」看哪些文件会冲突git fetch origin git checkout feature/user-search git merge --no-commit --no-ff origin/develop git diff --name-only --diff-filterU git merge --abort--no-commit --no-ff表示即使能快进也强制产生一次合并提交但不真正提交--diff-filterU只列出处于 unmerged 状态的文件。输出就是预计冲突的文件清单。确认完用git merge --abort回到干净状态。3.2 读懂冲突标记真正合并后冲突文件长这样 HEAD const users await userService.getList(params) ||||||| merged common ancestors const users await userService.fetchList(params) const users await userService.searchUsers(params) feature/user-search三段含义HEAD是当前分支你所在分支的版本|||||||是共同祖先版本之后是待合并分支的版本。有了祖先版本你就能判断两边各自改了什么而不是盲选。3.3 手动合并的三种决策面对一个冲突块只有三种结果留 HEAD、留对方、或者两者融合。判断依据是「祖先版本到两边各自的变化」情况祖先HEAD对方建议改同一行不同意图ABC融合通常两者都要一方删除一方修改A删除C确认删除意图必要时移植修改格式化 vs 逻辑改动A仅格式逻辑保留逻辑重跑格式化双方新增不同内容无新增 X新增 Y两者都保留融合时把标记行全部删掉只留最终代码。比如上面那段如果两个函数功能不同就都保留const users await userService.getList(params) const searchResult await userService.searchUsers(keyword)3.4 用 mergetool 处理复杂冲突冲突块多、跨文件时命令行逐块改效率低。直接调起配置好的工具git mergetoolVS Code 会以三栏或四栏形式展示 LOCAL、BASE、REMOTE、MERGED点选即可。处理完保存关闭Git 自动标记该文件已解决。如果某个文件你确定整份采用某一方可以跳过工具git checkout --ours src/config/production.json git checkout --theirs src/views/UserList.vue git add src/config/production.json src/views/UserList.vue--ours是当前分支--theirs是待合并分支别记反。3.5 解决后验证与提交所有冲突文件git add之后先别急着 commit跑一遍验证git status npx tsc --noEmit npx eslint src/ pnpm test四项都过再提交git commit -m merge: 合并 develop 到 feature/user-search解决 user.ts 与 UserList.vue 冲突3.6 回滚策略解决到一半发现方向错了分几种情况回滚# 还没 commit想放弃整个合并 git merge --abort # 已经 commit想撤销这次合并提交 git reset --hard ORIG_HEAD # 只想撤销某个文件的解决结果重新来 git checkout -m src/api/user.tsORIG_HEAD是 Git 在执行 merge、rebase 等操作前自动记录的指针比记 commit hash 方便。git checkout -m会把文件恢复成带冲突标记的状态让你重新解决。4. 验证请求确认冲突真的解决干净解决冲突最怕「以为解决了」。下面这套检查能兜住大部分遗漏。4.1 确认没有残留冲突标记git diff --check grep -rn \|\| src/ --include*.ts --include*.vuegit diff --check会报出空白错误和冲突标记残留。grep 是双保险尤其当冲突标记被误当成普通文本提交时。4.2 确认合并结果符合预期git log --oneline --graph -10 git diff origin/develop...HEAD --stat第一行看合并拓扑是否正常第二行看相对 develop 的改动范围是否合理。如果 diff 里出现大量你没动过的文件说明合并时可能误引入了对方的中间状态。4.3 跑一次完整构建pnpm build编译通过不代表逻辑正确但编译不过一定有问题。构建产物大小如果比合并前暴涨也要留意是不是重复引入了依赖。4.4 用 rerere 验证自动复用如果你开启了rerere第二次遇到相同冲突时git rerere status git rerere diffrerere status列出它准备自动解决的文件rerere diff展示它打算怎么解决。确认无误再git add。这一步相当于让 Git 帮你预演一遍。5. 本篇常见错排查实际操作中下面几个坑出现频率最高。5.1 冲突标记被提交进仓库现象代码里出现运行报语法错误。原因通常是git add时没检查或者用了git commit -a一把梭。排查git log -S --oneline找到引入的提交用git revert或修正后重新提交。预防手段就是上面 4.1 的git diff --check。5.2 ours / theirs 记反git checkout --ours在 merge 时指当前分支但在 rebase 时语义会反过来——rebase 的--ours是「正在被 rebase 到的目标分支」。这是最容易翻车的地方。建议 rebase 时不要用--ours/--theirs改用git checkout -m重新手动解决或者先git rebase --abort改用 merge。5.3 合并后测试全挂但代码看着没错多半是「语义冲突」两边代码单独都对合在一起逻辑冲突。比如 A 分支把函数改成异步B 分支在同步上下文里调它。这类冲突 Git 检测不到只能靠测试。所以 3.5 的测试步骤不能省。5.4 mergetool 打开是空白通常是mergetool.vscode.cmd里的$BASE变量在某些 Git 版本下为空导致。可以先确认git config --get mergetool.vscode.cmd如果工具对空 BASE 不友好把命令里的$BASE去掉改成三栏模式即可。5.5 rerere 自动解决错了rerere是「记录即复用」如果第一次解决时就是错的后面会一直错。发现后git rerere forget src/api/user.ts清掉该文件的记录重新手动解决一次让它重新学习。5.6 大文件冲突看不清几千行的配置文件冲突直接看标记会晕。用git diff --cc src/config/production.json--cc是 combined diff 模式只显示冲突区域不刷全文件。配合zdiff3的祖先段定位很快。6. 把冲突处理沉淀成团队习惯冲突解决本身是技术活但减少冲突是流程活。几个实测有效的做法每天开工先git fetch git rebase origin/develop让小冲突当天消化一个 PR 只做一件事避免大杂烩对高频共享文件比如http.ts、BasicLayout.vue约定改动前在群里说一声。如果你在团队里推广这套流程可以把第 2.4 节的.gitconfig片段放进仓库的docs/或 onboarding 文档新人 clone 后照着配一次。冲突处理工具链统一了review 时也少很多扯皮。对于需要长期在编码环节和 Git 打交道的场景比如频繁 rebase、批量解决冲突、生成合并提交信息可以了解一下 TaoToken 的 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan它面向的就是这类持续编码工作流。如果只是想先验证某个模型对冲突代码的理解能力可以直接在模型对话页https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chat贴一段冲突代码试试。接入自己的脚本或 CI 时API Key 在控制台https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys生成接口地址是 https://taotoken.net/api接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc。用 Claude Code 做 Git 操作的参考 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code。下一篇会讲 Git Blame 与历史追踪也就是冲突解决之后怎么追溯某一行代码是谁、在哪次提交里改的。