在维护 fork / 下游仓库 时,一个高频但痛苦的操作是:
定期 merge 上游仓库的更新,同时保留我们自己的实现。
冲突一多,手动点 IDE 的 “Accept Incoming / Current” 很快就变成体力活,而且极易误操作。
本文总结一套 工程上可靠、可自动化、可解释 的 Git 冲突处理方案,目标很明确:
不是我们维护的代码,全部直接用 upstream;
只有我们(团队)碰过的文件,才值得人工处理。
一、先说清一个事实:Git 并不知道“谁导致了冲突”
Git 只能判断:
- 同一文件
- 同一位置
- 双方都改了
它不知道“责任归属”,所以任何“自动判断谁该保留”的方案,本质都是:
基于历史规则的工程判断,而不是绝对真理。
我们的目标不是 100% 自动,而是:
让人只处理真正需要思考的冲突。
二、典型场景
- 你维护一个下游仓库(fork)
- 上游仓库持续更新
- 你和 1~2 个伙伴在下游实现了部分功能
- 很多文件你们从来没改过
- 但 merge 时仍然产生大量冲突
这类冲突 99% 应该直接用 upstream。
三、核心策略:用“我们是否改过”作为唯一判断标准
判断原则非常简单,也非常稳:
只要某个文件,在我们自己的提交历史里出现过,就不自动处理;
否则,一律使用 upstream。
这里的“我们”,简单说就是一个明确的作者白名单
四、操作流程
开始 merge(让 Git 停在冲突阶段)
Plain1git merge upstream/main
此时出现冲突是正常且正确的状态。
2️⃣ 自动处理「我们没改过的冲突文件」
下面这段脚本是全文的核心。
Plain1git diff --name-only --diff-filter=U > /tmp/conflicts.txt 2 3while read file; do 4 if git log BASE_BRANCH..HEAD --pretty="%an <%ae>" -- "$file" \ 5 | grep -E "(Your Name|you@email.com|other@email.com)" -q; then 6 echo "KEEP MANUAL (team touched): $file" 7 else 8 echo "USE UPSTREAM: $file" 9 git checkout --theirs "$file" 10 git add "$file" 11 fi 12done < /tmp/conflicts.txt
你需要替换的只有两点:
BASE_BRANCH:你开始维护下游仓库的分支或基准点grep -E:你和你同事的 作者名 / 邮箱白名单
查看当前分支的提交名单:
Plain1git shortlog -sne
3️⃣ 剩下的文件,才值得人工处理
Plain1git status
此时剩下的冲突文件,应该满足:
- 你或你同事确实改过
- 真正存在逻辑决策
4️⃣ 完成 merge
Plain1git commit
五、强烈建议的长期优化
1️⃣ 用 .gitattributes 固定掉“永远不该冲突”的文件
Plain1package-lock.json merge=theirs
2dist/** merge=theirs
这些文件以后 连冲突都不会出现。
2️⃣ 如果你们的代码集中在固定目录
直接把这些目录当成“人工白名单”,其余全部自动化。
六、总结
自动化 merge 的本质是为了区分:哪些代码是我们的资产,哪些不是。
当你把判断标准从“我感觉”变成“Git 历史”,你会发现:
- 冲突数量大幅下降
- 人只处理真正重要的地方
- 同步 upstream 变成一件可预期的事情