Fork 开源项目:选择性升级
Fork 了一个开源项目,上游还在不停更新:跟还是不跟?直接合并,上游的改动会冲掉本地增量;完全不跟,差距越拉越大,最后连安全修复都合不进来。我们的答案是选择性升级:不直接合并上游,按主题、风险和本地增量保护要求,手工吸收上游变更。
三条铁律
- 不直接合并上游:按上游 commit、发布说明和主题,一条一条手工吸收。已决定吸收的主题,按能力链路完整落地,不 cherry-pick 一半——半个功能比没有更危险。
- 保留本地语义:本地的选择性升级语义和 fork 增量是正式资产。版本号里保留本地标记(如
0.1.139+/PATCH_LEVEL=0.1.139),不为对齐上游而删除。PATCH_LEVEL不是版本号的装饰,它声明"这个版本含本地增量"。 - 不为对齐删除本地能力:不得为了跟上游一致,删除本地已有的能力、枚举、入口、配置项、翻译键或测试。对齐不是目的,正确才是。
高风险项:无确认不动
有些东西动了会影响线上行为,属于高风险项:平台枚举、账号与分组类型、权限和状态流转、计费与风控规则、公开接口契约、持久化字段。这些一律不得无确认删除或缩窄。语言包合并时,必须保留本地仍被引用的 key,不能拿上游语言包整体覆盖——丢一个翻译键,线上就多一个空白文案。
默认不吸收的也要明确写下来:Sponsors、README、Logo 这类更新,除非用户明确要求同步,否则跳过;上游的迁移编号重排、RBAC 相关删除类差异,除非单独确认删除和迁移方案,也不吸收。升级台账里记一笔"已评估、不吸收",比假装没看见强——半年后有人问"上游这个功能怎么没跟",台账能回答,而不是靠回忆。
一次版本吸收长什么样
以一次小版本吸收为例。低风险的网关与支付修复、网关兼容层正确性修复、计费支付规则修复,先吸收;公开错误契约(如 model_not_found)的修复,吸收——因为它影响调用方的错误处理逻辑;某个订阅支持按独立升级主题吸收并单独验证;指令 fallback 这类行为变更,吸收后要确认默认策略配置一起落地。
每一项后面都跟着"已吸收"三个字,而不是"已合并"。措辞的差别就是方法的差别:合并是把上游拿来,吸收是把需要的部分消化掉。
版本号里的本地标记
VERSION=0.1.139+、PATCH_LEVEL=0.1.139——加号和 PATCH_LEVEL 是本地选择性升级语义的一部分,不应为对齐上游而删除。
这套标记回答一个问题:"这个版本里到底含了什么?"上游版本号回答的是上游的进度,PATCH_LEVEL 回答的是本地的增量。两个保留,因为它们回答的是不同的问题。删掉本地标记去"对齐"上游,等于删掉了"我改过什么"的声明——以后出问题,连"这是上游的还是本地的"都分不清。
续审的教训:证据要拆开
后续一次大版本续审补了一个重要判断:选择性升级不仅是"挑 commit",还要把契约、编译、运行、发布和回滚的证据拆开。菜单视口、配额管理、自定义页面拖拽、长连接桥接、用量筛选、缓存/路由/限流/身份、模型资格管理,分别核对,不用一个总体验收替代各专项结论。
集成契约治理、单元测试、后端/前端门禁和编译迁移分开记录。通过的候选仍然要绑定分支、版本、环境和验证范围。发布完成、蓝绿切换、健康检查通过之后,真实供应方凭据、生产流量和高风险规则,继续留在发布后观察区——版本发布不能自动证明所有上游能力都已在生产等价成立。上游版本号和单次构建成功,都不能掩盖某个局部主题仍未闭环。
还有一条长期纪律:后续升级时,仍需逐个台账核对实际状态,不能凭一篇总结页推断"当前代码已包含某能力"。台账是活的,概念页是快照,别拿快照当台账用。
小结
Fork 的升级策略就三句话:按主题手工吸收,不直接合并;本地增量和高风险项无确认不动;每类证据分别验证,不拿版本号当质量证明。上游是别人的进度,生产是自己的责任。