一张「谁依赖谁」的图
让代码合并更聪明、更安全

把上游更新合并进你的 fork,最怕「顺序错了、接口断了、改漏了」。 这套方案用一张文件依赖图,在合并的每个环节做出更稳的决策—— 跨多种语言、结果可复现、所有增强都能按需开关。

5
种语言依赖提取
7
个协作环节受益
0
额外重型依赖
100%
结果可复现

01它能为你做什么

不谈底层实现,先看它在一次真实合并里替你把住的几道关。

🧭

合并前 · 定序

让被很多文件依赖的「基础文件」先合并,避免合到一半时因为依赖还没到位而构建失败。

🛡️

合并中 · 保接口

在改动「被大量引用」的代码前提醒:保留对外接口,别让下游一片飘红。

🔍

合并后 · 查漏改

上游改了某个接口,自动找出那些还在用旧用法、却忘了一起更新的文件。

🎯

审查 · 聪明聚焦

审查大文件时,只展开真正相关的代码片段(含被外部引用的公共符号),省时也省成本。

🧑‍⚖️

人工 · 给依据

需要人来定夺时,决策卡直接告诉你「这文件牵一发动多少」,判断更快更稳。

🌐

通用 · 可配置

Python / JavaScript / TypeScript / Go 通用;所有增强默认安全,可一键开关。

02它到底是什么

两个最关键的概念:依赖图本身,以及它「只增不减风险」的安全守则。

① 一张文件依赖图

节点是文件,表示「A 引用 / 继承 / 调用 B」。 它不分析整个仓库,只聚焦本次改动文件及其邻居,构建成本可控,并随合并进度自动保存、断点续跑。

view api db.pyhub util log

db.py 被很多文件依赖 = 枢纽文件(God Node),改它影响面最大、最该谨慎。

② 只增不减的安全守则

每条边都带一个可信度标签。核心守则:依赖图只会提高谨慎度, 绝不因为「图里没找到关系」就放松风险——宁可多审,不可漏审。点下面切换看每一档怎么用:

03方案设计 · 建一次,全局共享

整套方案的核心设计:依赖图只在开始时构建一次,之后作为一份只读的共享资产, 被合并流程里 7 个不同环节各取所需。一处构建、多处复用,互不干扰。点任一环节看它怎么用图:

04核心技术点

支撑上面这些能力的四组关键技术,以及为什么这样设计。

  • 多语言、按扩展名路由:JavaScript / TypeScript / TSX / Go 用业界成熟的 tree-sitter 语法解析;Python 用语言内置的 AST。装不上解析器时自动降级、不报错。
  • 只建子图,不扫全仓:合并是一次性、聚焦改动的操作,所以只解析「改动文件 + 它的若干跳邻居」,几百个文件级别,秒级完成。
  • 精确到符号:不止知道「文件 A 依赖文件 B」,还知道「A 用到了 B 里的哪个函数 / 类」——这让后续的「聚焦」和「漏改检查」更准。
  • 每条边带可信度:AST 能确定的标「确定」,调用推断的标「推断」,动态 / 反射调用的标「不确定」——下游据此决定能不能据它做自动决策。
  • 拓扑排序定序:按依赖关系排合并顺序——被依赖的基础文件 / 基类,排在依赖它们的文件之前,避免中间状态构建不过。
  • 影响半径与枢纽识别:统计一个文件被多少文件(直接 + 间接)依赖。影响面越大,合并复杂度评分越高;超过阈值即枢纽文件,自动加一档风险。
  • 顺序复核:计划生成后再独立复核一遍,若发现「依赖者被排在被依赖者前面」这类危险顺序,会标记出来要求修正。
设计取舍:依赖顺序只作为同等风险内的次级排序,不会推翻「安全的先合」这一主原则;风险评分也只做加法,不会因为图而调低。
  • 保接口:执行合并时,先查「谁在依赖这个文件」;若改签名 / 删文件,会提示保留对外接口、列出受影响的下游。
  • 漏改防护:上游改了某接口后,沿依赖关系找出仍在用它、却没被一起更新的文件,标记为需要处理。
  • 谨慎度注入:当冲突发生在枢纽文件、影响面很大时,引导分析走更保守的合并策略。
  • 人工决策卡:交给人定夺时,卡片标出「N 个依赖者 / 影响半径 M / 是否枢纽」,让人快速掂量分量。
  • 上下文智能聚焦:审查大文件时只展开真正相关的代码块;那些被其他文件引用的公共符号即便不在本次改动里,也会被保留,不会被压缩掉。
  • 图驱动模块划分(可选):用真实的依赖边界把文件聚成模块,比单纯按目录结构更贴合实际——见下方互动演示。
  • 别名 / monorepo 路径解析(可选):识别 tsconfig 路径别名、go.mod 模块、monorepo 工作区,把别名 import 也连成依赖边——见下方互动演示。
  • 经验沉淀(可选):把「这是枢纽文件」「这两个目录意外耦合」等结论记成持久经验,下次合并可直接复用。
带「可选」的能力默认关闭,开启与否都不影响主流程的稳定性——这正是下一节「设计原则」要讲的。

05亲手玩:两个核心算法

下面两个演示跑的是和系统里同款的真实算法逻辑,不是录像——点一点就懂。

🧬图驱动模块划分 · 标签传播

每个文件先各成一组,每轮「随大流」——把自己的归属改成邻居里最多见的那一组。 互相引用密集的文件会自然聚到一起。点「下一轮」看它如何收敛成清晰的模块:

收敛后每组按成员的所在目录取个好认的名字。整个过程确定可复现;没有依赖边时则退回按目录分组——安全降级。

🔗别名 / monorepo import 解析

现代项目大量用别名或裸路径 import(比如 @app/util)。 不解析的话,这些其实是内部依赖的关系就被当成第三方丢掉了。选一条 import,看它如何被还原成仓库里的真实文件:

已识别的映射规则: @app/* → src/app/* @/* → src/* go.mod: github.com/org/repo 工作区包 @org/ui → packages/ui

06四条贯穿始终的设计原则

这些原则让一套「通用」的合并工具敢于引入依赖图,而不必担心拖慢或锁死在某一个项目上。

原则

🛡️ 安全降级

图为空、配置缺失、解析失败、解析器没装——统统回退到原有行为,绝不报错。最多少一点增益,绝不多一个问题。

原则

🎚️ 默认中性、可开关

模块划分、别名解析等增强默认关闭。不开启时,行为与没有依赖图时完全一致,保持对任意项目通用。

原则

📦 零额外重依赖

模块划分用标准库实现的「标签传播」,而非引入庞大的图计算库;别名解析也只用内置能力。体积小、好理解。

原则

🎯 结果可复现

排序、聚类、解析全部确定——同样的输入永远得到同样的结果,支持断点续跑,不会两次跑出不同的计划。

技术选型一览

能力采用的技术
多语言依赖提取tree-sitter(JS/TS/Go)+ Python 内置 AST,缺失自动降级
合并定序依赖图拓扑排序,作为同风险内的次级排序键
枢纽识别影响半径(传递依赖计数)+ 可配置阈值
模块划分标准库实现的标签传播,非重型图库
别名解析tsconfig 路径 / go.mod 模块 / 工作区包名
持久化随合并 checkpoint 自动保存与恢复