维护一个上游分支:每次同步大版本要动哪些地方
维护一个「跟着上游走、又带自己改动」的分支,最容易低估的不是冲突数量,而是上游顺手把你依赖的基础设施也改了。
这个月的一次上游同步可以当样本:5231 个文件、+21.6 万 / −61.1 万,看着吓人,但真正与本分支有交集的只有约 80 个文件、11 个内容冲突。冲突一个下午就解完了,剩下的时间全花在「上游把测试框架整个换掉」这件事上。
这篇按这次的实际顺序记一遍。
一、先量化规模,再决定怎么打
动手前先算出三个数字,它们决定策略完全不同:
| 指标 | 这次的值 | 意味着什么 |
|---|---|---|
| 上游改动规模 | 5231 文件 / +21.6 万 −61.1 万 | 是一次大重构,不是补丁 |
| 与本分支交集 | 约 80 个文件 | 冲突面可控 |
| 实际冲突 | 11 个内容冲突 | 一下午能解完 |
如果交集只有 80 个文件,就不要去做「全部 rebase 重放」——那是几千个文件的工作量。用 git merge 把这个上游版本整体合进来,只处理交集部分。
顺带一句:改动行数是负数(−61.1 万),说明上游删得比加得多。删除比新增更容易伤到你——删掉的文件可能是你分支里还在用的东西。
二、冲突好解,难的是「基础设施被换掉」
11 个冲突按常规处理:保留本分支的策略常量、品牌改动和自有发布链路,其余跟上游走。真正花时间的是三类「不算冲突但照样让你跑不起来」的变化。
① 测试命令直接消失。
上游删掉了 test:unit / test:unit:affected,连 scripts/run-affected-unit-tests.mjs 一起删了,改成 test:engineering 系列,并且——不再自动发现 packages/**/test,改由 scripts/engineering-test-files.json 这个显式清单管理。
后果很直接:本分支为了保住自己的策略和品牌断言而写的那些测试,不在清单里就等于从来没跑过。表现是「按新命令跑,全绿通过」——但你的测试一条都没执行。
修法是逐个确认本分支新增的测试文件,手动登记进清单。这一步没有报错提示,只能靠自己数。
② API 改名。
readRequestVerificationConfig 变成 readValidatedRequestVerificationConfig,还新增了 ApiError 类型。这种改名在 TypeScript 里会明确报错,好办;要小心的是只是行为变了、签名没变的那类。
③ 新增功能插进了你改过的位置。
上游在桌面更新流程里新增了 verifyWindowsAuthenticodeSignature,而这正是本分支「更新只走自有 Release」的改造点。合并时不显式保住 checkSelfHostedRelease 的分流,上游那套判断就会把你的分流顶掉——编译能过、类型也对,只是行为悄悄变回上游的。
这类「语义冲突」比文本冲突危险得多,只能靠合并后逐处复核改过的文件。
三、旧测试:删一部分,改一部分
上游既然重构了测试,本分支原有的测试自然有一批不兼容。这次共 13 个,分两类处理:
按上游退役删掉(9 个):
- 7 个 UI 测试依赖
@testing-library/react,而上游把这个依赖移除了; - 1 个测的是「厂商更新链路」的就绪状态,而本分支已经明确关闭了那条链路——测的对象不存在了;
- 1 个的旧断言假设「渲染进程自己判断 stale」,上游改成「由 Main 判断」,断言前提已经不成立。
改断言后保留(4 个):仍然有效的实现测试,按上游新行为逐个改。
关键判断:「不兼容」不等于「该删」。依赖被移除了、被测对象被关了,这是真该删;只是行为变了,就该改断言留下。
还有一条顺序上的讲究:如果一款文件同时是「上游删除、本地修改」,先保留本地版本,统一验证完再按退役逐批清理。一次性按上游删,会把本分支的策略覆盖丢光,而且丢了你也不知道——因为测试也一起没了。
四、拿不到日志时,怎么记遗留
发布这次没全绿:Build Linux-rpm-arm64 这个 job 失败。
gh run view --log-failed 拉日志超时,只能定位到「失败发生在容器内 pnpm install 阶段」,后面的打包、校验、收集产物全是 skipped。根因没查到。
这种时候的处理原则是:不猜,写清楚边界。
- Release 说明不能写「Linux x64 与 arm64 均可用」——资产里确实缺 arm64 的 rpm;
- 遗留条目要写清「哪一步失败、卡在哪个阶段、为什么没继续查、下轮怎么补」。
比”我以为只是网络抖动”要好得多:下一个看这条记录的人(通常是三个月后的自己)能直接接着查。
五、一条给下次的检查清单
同步完成后,除了「测试全绿」,还要确认这几件事:
- 测试真的跑了——新框架若是显式清单制,先数一遍本分支的测试有没有登记;
- 改过的文件逐个复核行为——尤其是被上游新增逻辑插进来的位置(更新流程、鉴权、配置读取);
- 上游新增的网络出口有没有接到你的策略门禁上——这次上游加了一个客户端配置下载边界,目前没有调用方,但将来一接入就会绕过本分支的「关闭官方远端配置」策略,必须提前登记成待办;
- 元数据别写错——同步记录的日期、版本号这种小地方,写错了会让下一个人误判范围(这次就把日期写成了一个月前);
- 产物清单要和发布说明对得上——少一个架构的包,说明里就不能说「双架构可用」。
总结
跟着上游走的分支,维护成本不在「解冲突」,在这三件事:
- 上游会改你的基础设施——测试怎么跑、怎么发现,比业务代码更影响你;
- 语义冲突比文本冲突危险——编译通过、测试通过,行为却变回上游的;
- 拿不到证据时,把边界写清楚,不要用推测填补空白。
一句话:同步大版本之后,先验证「你的测试还在跑」,再验证「测试全绿」。