graphify 跨仓库图谱实战:GitHub 克隆(clone)与跨仓库合并(merge-graphs)完整指南
【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify
当一次提问的目标是「一个或多个 GitHub 仓库 URL」或「若干需要合并进同一张图的本地子目录」时,graphify 会加载 github-and-merge.md 这张参考卡来驱动流程。本指南以该参考卡为主线,结合 graphify 仓库中
graphify clone与graphify merge-graphs的 CLI 实现源码,逐条还原「克隆 → 提取 → 合并 → 查询」的完整链路,讲清缓存复用、输出目录隔离、repo 属性标记与合并时的各类归一化处理。读完你可以直接上手把多个仓库/子模块做成一张可查询的统一知识图谱,并准确预判每条命令会写入哪里、合并时会发生什么。
这张参考卡在什么场景下被触发
graphify以「/graphify」技能的形式注入到 Claude Code、Cursor、Codex、Gemini CLI 等各平台。当用户的任务满足以下两类输入之一时,Agent 会加载 github-and-merge.md(该文件在agents、claude、vscode、codex等各平台目录下内容一致,例如 agents 版):
- 用户传入了一个或多个
https://github.com/...形式的 URL,要求分析远程仓库; - 用户点名了若干需要合并进同一张图的本地子文件夹(典型如 monorepo 的多个 package、或一个多服务代码库的若干目录)。
这两个入口最终都汇合到同一条命令:graphify merge-graphs。区别只在前置的「原料准备」阶段——URL 需要先克隆到本地,子文件夹则直接扫描。
全景流程:两条入口,一个合并终点
GitHub URL ──► graphify clone ──► 本地路径 ──► graphify extract(逐仓) ──► 每仓 graph.json 本地子目录 ──► graphify extract ./xxx/ ──► ./xxx/graphify-out/graph.json(逐目录) │ ▼ graphify merge-graphs <g1> <g2> ... --out merged.json │ ▼ graphify query(直接命中合并图,无需重提取)第一步:用graphify clone拉取 GitHub 仓库(仅当输入为 URL)
单仓库:捕获返回的本地路径
参考卡给出的最小用法是:
LOCAL_PATH=$(graphify clone <github-url> [--branch <branch>]) # 后续所有步骤都以 LOCAL_PATH 为扫描目标clone命令之所以能被$( )捕获,是因为实现里把目标路径打印到了 stdout——见 graphify/cli.py 的elif cmd == "clone"分支:解析完参数后调用_clone_repo(...),紧接着print(local_path)。同时它也会友好地输出Ready at: <dest>这样的进度行。
克隆位置与缓存复用机制
参考卡明确约定:graphify 克隆到~/.graphify/repos/<owner>/<repo>,且重复运行时复用已有克隆。对应的底层实现是_clone_repo(graphify/cli.py),几个值得注意的实现事实:
- URL 归一化:先去掉尾部
/,若不以.git结尾则补上.git用于git clone,同时保留去掉后缀的版本供后续解析 owner/repo(github.com://([^/]+?)(?:\.git)?$正则),识别不了会直接报错退出; - 缓存命中即增量更新:目标目录已存在时不再
git clone,而是执行git -C <dest> pull(指定--branch时追加origin -- <branch>),因此重复跑同一 URL 成本极低; - 全新克隆为浅克隆:
git clone --depth 1,如需指定分支会追加--branch <branch>; - 参数校验:分支名以
-开头会被拒绝,避免参数注入。
CLI 帮助文本中的完整签名是graphify clone <github-url> [--branch <branch>] [--out <dir>](见 graphify/main.py),其中--out <dir>可以覆盖默认的~/.graphify/repos/<owner>/<repo>缓存位置(源码中dest的默认分支即Path.home() / ".graphify" / "repos" / owner / repo)。
多仓库(跨仓图谱):逐仓克隆、逐仓提取、最后合并
参考卡给出的多仓流程是:
# 逐个克隆(各自进入 ~/.graphify/repos/<owner>/<repo>) graphify clone <url1> # → ~/.graphify/repos/<owner1>/<repo1> graphify clone <url2> # → ~/.graphify/repos/<owner2>/<repo2> # 对每个本地路径跑完整管线,各自产出 graph.json # 然后合并: graphify merge-graphs \ ~/.graphify/repos/<owner1>/<repo1>/graphify-out/graph.json \ ~/.graphify/repos/<owner2>/<repo2>/graphify-out/graph.json \ --out graphify-out/cross-repo-graph.json关键点在于每一仓先独立产出自己的graph.json,再交给merge-graphs合并。合并后的图里,每个节点都带repo属性,可以按来源过滤节点——这正是跨仓场景下追溯「这段代码属于哪个仓库」的基础。
合并阶段:graphify merge-graphs的实现细节
用法与参数
merge-graphs的命令行形态是(实现见 graphify/cli.py):
graphify merge-graphs <graph1.json> <graph2.json> [...] [--out merged.json]解析规则如下表:
| 项 | 说明 |
|---|---|
| 位置参数 | 依次为待合并的graph.json路径,至少两个,否则打印用法并退出 |
--out <path> | 输出路径;不传时默认为graphify-out/merged-graph.json(_GRAPHIFY_OUT / "merged-graph.json") |
| 输入存在性 | 任一输入文件不存在即报错退出 |
| 规模保护 | 每个输入都会先过_enforce_graph_size_cap_or_exit的大小上限检查 |
| 成功输出 | Merged N graphs -> X nodes, Y edges+Written to: <out> |
需要注意:参考卡示例中输出文件名取的是cross-repo-graph.json(自定义--out的典型用法),而默认文件名是merged-graph.json——二者等价,只是路径不同。
节点如何区分来源:repo 属性 + 唯一前缀标签
参考卡说「合并图的每个节点带repo属性」,而实现上为了做到这一点做了两层工作(都在 graphify/cli.py):
_repo_tags(graph_paths)(定义于 graphify/build.py)为每个输入图生成互不相同的仓库前缀标签。直接用目录名做前缀并不安全——例如src/graphify-out和frontend/src/graphify-out都叫src,同名前缀会让不同仓库的同名节点 id 静默撞车、错误合并实体;源码注释记录了这一坑(#1729);prefix_graph_for_global(graphify/build.py)把每个输入图的节点 id、边、超边统一加上自己的前缀标签,合并图的社区 id 也会做偏移去重(每个仓库从 0 编号,不偏移就会在聚合视图里把无关社区焊成一个 meta-node,#3014)。
合并前的归一化:保证异构输入能安全 compose
被合并的多个graph.json未必出自同一条产物路径(可能是纯 AST 提取,也可能是带 LLM 语义标注的完整提取),因此merge-graphs在真正合并前做了一系列归一化(graphify/cli.py):
edges/links兼容:新版本以links键写入,老版本可能残留edges,缺links时自动补;- 有向信息保留:每条 link 先记录
_src/_tgt标记再进入无向的node_link_graph,合并写出后再从标记恢复真实方向(#2261/#2309 等历史问题在注释中均有引用); - 超边双槽位:
node_link_data只把超边嵌在graph.hyperedges里,老 reader 只认顶层键,因此合并输出会同时写两个槽位,保证所有写入方读取一致(#2484/#2485); - 图类型统一:有向/无向、简单/多重图被统一转成无向的简单
Graph——「跨仓视图本来就是无向的」,这是避免nx.compose因图类型不一致直接崩溃的前提(#1606)。
合并之后的跨仓连接:共享类型声明链接
一个容易被忽略的细节:所有节点 id 都被打了仓库前缀后,两个仓库共同声明的契约类型会变成两个互不相连的孤立节点。为此merge-graphs在 compose 之后调用link_shared_type_declarations(实现在 graphify/cross_repo_types.py,对应问题 #3007):它只连接「命名空间 + 类型名完全一致、且至少跨越两个仓库」的类型声明节点,边上下文中标注cross_repo,让你能沿着图跨过仓库边界做遍历——例如生产方与消费方各自维护一份同名 contract 时。执行时会打印类似linked N type declaration(s) shared across repos的信息。
同样地,被收集起来的各仓库超边不会像早期版本那样被后一个输入覆盖(#1691 的教训),而是去重后统一重新挂接。最终结果通过write_json_atomic原子写盘(graphify/cli.py),避免写一半留下损坏文件。
多本地子文件夹场景:monorepo / 多服务布局
为什么不能对每个子目录各跑一次「/graphify 技能」
参考卡给出了一个非常实际的陷阱:技能管线把所有中间与最终产物都写到当前工作目录的graphify-out/。如果对./core、./service、./platform各跑一次 /graphify,它们会互相覆盖同一个输出目录。因此子目录场景要绕过技能,改用 CLI 直接提取。
CLI 提取会把graphify-out/放到被扫描路径内部
这正是graphify extract与技能管线在输出位置上的核心差异,源码依据在 graphify/cli.py:
# Resolve output dir. The user-facing contract is "<out>/graphify-out/" # so a fresh checkout writes graphify-out/ at the project root, matching # the skill.md pipeline. out_root = (out_dir.resolve() if out_dir else target) graphify_out = out_root / _GRAPHIFY_OUT即:未显式给--out/--output时,输出目录 =被扫描的 target 路径 +graphify-out/(_GRAPHIFY_OUT默认就是字符串graphify-out,也可用环境变量GRAPHIFY_OUT覆盖为任意相对/绝对路径,参见 graphify/paths.py 与out_path定义)。于是每个子目录的图互不干扰:
graphify extract ./core/ # → ./core/graphify-out/graph.json graphify extract ./service/ # → ./service/graphify-out/graph.json graphify extract ./platform/ # → ./platform/graphify-out/graph.json # 也可视你已配置的 API Key 追加: # --backend gemini|kimi|claude|openai|deepseek|ollama # 最后在项目根目录统一合并: graphify merge-graphs \ ./core/graphify-out/graph.json \ ./service/graphify-out/graph.json \ ./platform/graphify-out/graph.json \ --out graphify-out/graph.json关于extract命令本身的定位,源码注释说得很清楚(graphify/cli.py):它是给 CI/脚本用的无头全流程提取,依次执行 detect → 代码的 AST 提取 → 文档/PDF/图片的语义 LLM 提取 → 合并 → build → cluster → 写出产物。--backend参数是可选的:不指定时从DEEPSEEK_API_KEY等环境变量中自动探测(见 graphify/cli.py 附近的说明),指定时按gemini|kimi|claude|openai|deepseek|ollama之一选用。除--backend外,extract还支持--model、--mode deep、--out/--output、--no-cluster、--code-only、--no-gitignore、--max-workers、--token-budget等开关(完整帮助在 graphify/cli.py),适合在大仓上控制资源占用。
注意 merge 输出名这里直接复用graphify-out/graph.json——只要它存在,后续所有「基于该代码库的提问」就会命中快速路径。
合并完成后的「快速路径」:直接用graphify query
参考卡最后强调:一旦graphify-out/graph.json存在,快速路径就接管了——任何代码库提问都直接对该合并图跑graphify query,不再重提取,也不再受规模门槛约束。
从 CLI 形态看,query的默认图路径正是graphify-out/graph.json(帮助文本见 graphify/main.py),并且可用--graph <path>指向任意位置的图。所以当你把跨仓合并结果写到graphify-out/graph.json(而不是自定义的merged-graph.json之类的文件名)时,之后graphify query "..."无需任何额外参数就能命中合并后的跨仓图。query的机制是对图做 BFS/DFS 遍历并沿边聚合上下文(支持--dfs、--budget N、--context C等),这意味着「只查一次,不用把三个子仓库再各提取一遍」。
工程实践小结与选择依据
| 你的输入 | 推荐路径 | 输出位置 |
|---|---|---|
| 单个 GitHub URL | graphify clone <url>→ 对返回的LOCAL_PATH跑 /graphify | 克隆缓存~/.graphify/repos/<owner>/<repo>;图谱默认graphify-out/graph.json |
| 多个 GitHub URL(跨仓) | 逐仓clone+ 逐仓提取,再merge-graphs | 合并图可用--out指定 |
| 多个本地子目录(monorepo/多服务) | 逐目录graphify extract ./xxx/,再在根目录merge-graphs | 每目录内部./xxx/graphify-out/,互不覆盖 |
再补充几个实战要点:
clone是可重复执行的:重复克隆同一 URL 会退化为git pull,天然适合 Agent 多次运行;merge-graphs至少需要 2 个输入图,输出默认落在graphify-out/merged-graph.json,建议显式--out以控制最终查询目标;- 合并图的每个节点保留
repo属性,可用于按来源过滤;跨仓库共享的类型声明会被自动加上cross_repo上下文边,供图遍历跨越仓库边界; - 若各子目录的
graphify-out目录名有重复(如多层嵌套都叫src),合并工具会自动改用不冲突的仓库标签前缀,并在终端打印提示。
延伸阅读:参考卡与可验证的源码/测试入口
- 参考卡本体(各平台同构):vscode 版、claude 版、codex 版;技能的装配方式见 skill.md 与各平台
skill-*.md(如 skill-vscode.md); - CLI 实现:clone 命令与
_clone_repo、merge-graphs 分支、extract 全流程入口; - 前缀与标签生成:prefix_graph_for_global、distinct_repo_tags;
- 跨仓共享类型连接:link_shared_type_declarations;
- 输出目录契约:GRAPHIFY_OUT 与 out_path、graphify/paths.py;
- 相关测试:合并命令行为见 test_merge_graphs_cli.py,跨仓共享类型见 test_cross_repo_shared_types.py,规模门禁相关见 test_build_merge_shrink_guard.py。
【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考