维护一个上游分支:每次同步大版本要动哪些地方

维护一个「跟着上游走、又带自己改动」的分支,最容易低估的不是冲突数量,而是上游顺手把你依赖的基础设施也改了。

这个月的一次上游同步可以当样本: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;
  • 遗留条目要写清「哪一步失败、卡在哪个阶段、为什么没继续查、下轮怎么补」。

比”我以为只是网络抖动”要好得多:下一个看这条记录的人(通常是三个月后的自己)能直接接着查。

五、一条给下次的检查清单

同步完成后,除了「测试全绿」,还要确认这几件事:

  1. 测试真的跑了——新框架若是显式清单制,先数一遍本分支的测试有没有登记;
  2. 改过的文件逐个复核行为——尤其是被上游新增逻辑插进来的位置(更新流程、鉴权、配置读取);
  3. 上游新增的网络出口有没有接到你的策略门禁上——这次上游加了一个客户端配置下载边界,目前没有调用方,但将来一接入就会绕过本分支的「关闭官方远端配置」策略,必须提前登记成待办;
  4. 元数据别写错——同步记录的日期、版本号这种小地方,写错了会让下一个人误判范围(这次就把日期写成了一个月前);
  5. 产物清单要和发布说明对得上——少一个架构的包,说明里就不能说「双架构可用」。

总结

跟着上游走的分支,维护成本不在「解冲突」,在这三件事:

  • 上游会改你的基础设施——测试怎么跑、怎么发现,比业务代码更影响你;
  • 语义冲突比文本冲突危险——编译通过、测试通过,行为却变回上游的;
  • 拿不到证据时,把边界写清楚,不要用推测填补空白。

一句话:同步大版本之后,先验证「你的测试还在跑」,再验证「测试全绿」。