首页
/ graphify 钩子集成实战:git post-commit 自动重建知识图谱与 Claude Code 原生接入

graphify 钩子集成实战:git post-commit 自动重建知识图谱与 Claude Code 原生接入

2026-09-06 13:55:56作者:蔡怀权

本文基于 graphify 的参考文档 hooks.md,讲解 graphify 的两类"免值守"集成方式:通过 graphify hook install 安装 git post-commit/post-checkout 钩子,让每次提交和切分支后自动增量重建知识图谱;以及通过 graphify claude install 把 graphify 规则写进项目 CLAUDE.md 并注册 Claude Code 的 PreToolUse 钩子,让 Claude Code 会话在回答代码库问题前自动查图、在改动代码后自动重建。读完本文,你可以掌握钩子的安装/卸载/状态检查全流程、钩子内部的重建机制、全部相关环境变量与配置项,以及 Claude 原生集成的写入与移除细节,并能从源码层面理解每一步的实际行为。

一、两类集成机制的定位

graphify 的核心产物是 graphify-out/ 目录下的 graph.json(可查询知识图谱)与 GRAPH_REPORT.md(架构报告)。默认情况下,图需要手动构建和更新。参考文档给出的两条"自动化通道"分别解决两个问题:

  • git 钩子graphify hook install):解决"图过期"问题。每次 git commit 后自动检测变更的代码文件、重跑本地 AST 抽取并重构建图,无需任何后台常驻进程,每次提交只触发一次,编辑器无关;
  • 原生 CLAUDE.md 集成graphify claude install):解决"Claude 不知道图的存在"问题。一次性把 ## graphify 规则段写入本地 CLAUDE.md,让 Claude Code 在之后的每个会话里都默认"先查图再回答",无需每次手动执行 /graphify

两者的命令入口在 CLI 入口 中注册,帮助文本明确列出了 hook install / hook uninstall / hook statusclaude install / claude uninstall 子命令。

二、git 提交钩子:自动增量重建

2.1 安装、卸载与状态检查

参考文档给出的三个命令覆盖了钩子的完整生命周期:

graphify hook install    # install
graphify hook uninstall  # remove
graphify hook status     # check

这三个子命令最终委托给 hooks.py 中的 install / uninstall / status 函数(见 cli.py 的 hook 分支)。从源码结构看,实际安装动作比文档描述的"装一个 post-commit 钩子"更完整:

  1. post-commit 钩子(标记块 # graphify-hook-start# graphify-hook-end,见 hooks.py#L8-L9):每次提交后增量重建;
  2. post-checkout 钩子(标记块 # graphify-checkout-hook-start):仅在切换分支(第三个参数为 1)且 HEAD 实际变化时触发全量重建;
  3. graph.json 合并驱动:通过 git config 注册 merge.graphify.driver,并在 .gitattributes 追加 graphify-out/graph.json merge=graphify,让被跟踪的 graph.json 在合并时走 union merge 而非产生冲突(见 _register_merge_driver)。

安装策略上,_install_hookhooks.py#L540-L568)实现了参考文档中"If a post-commit hook already exists, graphify appends to it rather than replacing it"的行为:

  • 目标钩子文件不存在:写入完整脚本并 chmod 0o755
  • 文件存在但没有 graphify 标记:把 graphify 块追加到已有内容之后(例如已有 husky 或其他工具装的钩子不受影响);
  • 文件存在且已有 graphify 标记块:用新脚本原位替换旧块,升级安装时自动刷新脚本内容。

卸载时 _uninstall_hook 用 start/end 标记做正则切除;若切除后只剩 shebang 行则直接删除整个钩子文件,其余第三方内容原样保留。

钩子目录解析尊重 core.hooksPath(例如 Husky 场景):_hooks_dir 通过 git rev-parse --git-path hooks 让 git 自己解析路径,并在解析结果指向 .husky/_(Husky 9 的自动生成目录)时回退到用户可编辑的 .husky/ 父目录(见 _user_hooks_dir)。

2.2 一次 commit 之后发生了什么

参考文档给出的流程描述是:"检测到哪些代码文件变了(git diff HEAD~1),对这些文件重跑 AST 抽取,重建 graph.jsonGRAPH_REPORT.md;文档/图片变更被钩子忽略,需要手动跑 /graphify --update"。对照生成的钩子脚本 _HOOK_SCRIPT,实际执行链更细:

  1. 确定性固定export PYTHONHASHSEED=0,消除 networkx louvain 社区划分因字符串哈希随机化导致的运行间抖动,保证 graphify-out/ 可复现;
  2. Windows/MSYS 保护:在 WINDIRMSYSTEM 环境下默认 GRAPHIFY_MAX_WORKERS=1(串行重建),规避 GUI 客户端/Agent shell 继承的脆弱管道句柄;显式设置的 GRAPHIFY_MAX_WORKERS 仍然优先;
  3. 交互态跳过:处于 rebase(rebase-merge/rebase-apply)、merge(MERGE_HEAD)或 cherry-pick(CHERRY_PICK_HEAD)时直接 exit 0,避免未暂存改动阻塞 --continue
  4. worktree 守卫git worktree add 创建的链接 worktree 中 git-dir != git-common-dir,此时直接跳过,防止从 worktree 写出"脏"的增量图并与 CI 的 git clean 竞争(_WORKTREE_GUARD);
  5. 变更收集CHANGED=$(git diff --name-only HEAD~1 HEAD ...),即文档所述的 git diff HEAD~1;若本次提交只改了 graphify-out/ 下的产物(图输出被纳入版本跟踪的常见情形),直接退出,避免"重建产物又触发重建"的循环;
  6. Python 解释器探测:钩子内嵌了一段四重探测逻辑(_PYTHON_DETECT),依次尝试:安装时钉死的 sys.executable 绝对路径(__PINNED_PYTHON__ 占位符在安装时被替换)→ graphify-out/.graphify_python 记录文件 → PATH 上的 graphify launcher(含 shebang 解析、Windows Scripts/ 布局)→ uv tool 环境目录扫描 → 最后才回退 python3/python。每个候选都通过 importlib.util.find_spec('graphify') 轻量探测(只定位不导入),全部失败时打印提示并 exit 0——钩子永远不让 commit 失败
  7. 后台分离重建:钩子不直接跑重建,而是通过 _LAUNCHER_TEMPLATE 用 Python 自身完成"detached launch":POSIX 用 start_new_session,Windows 用 CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP | CREATE_BREAKAWAY_FROM_JOB。这一步替换了早期的 nohup ... & 方案——Git for Windows 的 MSYS shell 没有 nohup/setsid,旧方案会让重建静默不执行而 git 仍返回 0(见 graphify/hooks.py#L240-L256 的注释)。结果是 git commit 立即返回,重建日志追加写入 ~/.cache/graphify-rebuild.log

后台子进程执行的核心逻辑是 _REBUILD_BODY_COMMIT:读取环境变量 GRAPHIFY_CHANGED 得到变更文件列表,调用 watch.py 的 _rebuild_code 完成"仅重抽变更文件 + 保留旧节点 + 聚类 + 报告"的增量重建,随后以 best-effort 方式(失败绝不影响钩子)调用 reflect 刷新工作记忆经验文档。

_rebuild_code 的 docstring(watch.py#L1284-L1306)还揭示了几个值得注意的机制:

  • 传入 changed_paths 时为增量模式:只重抽这些文件,未变更文件的节点从现有图中保留,已删除路径从保留集剔除;changed_paths=None(post-checkout 用的正是这种形式)时为全量重抽;
  • force=True(由 GRAPHIFY_FORCE 触发)会绕过 to_json 的节点数安全检查,让"合理地删了大量代码"的重建可以合法地写出更小的图;
  • 重建带 per-repo 非阻塞 flock:多仓库同时提交、或 commit+checkout 背靠背触发时不会堆叠,抢不到锁的增量变更会被排队,锁持有者重建完成后合并排队的变更集再补一轮重建。

2.3 post-checkout:切分支也自动重建

参考文档只讲了 commit 钩子,但源码里同时安装的 post-checkout 钩子(_CHECKOUT_SCRIPT)值得单独说明:

  • 只在 BRANCH_SWITCH=1 时运行,git checkout <file> 这类文件级检出不会触发;
  • PREV_HEADNEW_HEAD 相同(如 git checkout -b 无起点)的 no-op 切换直接退出;
  • 要求 graphify-out/ 已存在——即项目至少构建过一次图,否则跳过;
  • 同样遵守 GRAPHIFY_SKIP_HOOK=1、rebase/merge/cherry-pick 跳过与 worktree 守卫;
  • 走全量重建路径(不传 changed_paths),因为切分支可能任意改动大量文件。

2.4 环境变量与项目配置一览

结合钩子脚本与 _load_graphifyrc_apply_resource_limits,可以整理出钩子链路的全部可配置项:

变量 / 配置 默认值 作用
GRAPHIFY_SKIP_HOOK 0 设为 1 时 post-commit 与 post-checkout 均跳过重建(例如 CI 上不想跑)
GRAPHIFY_REBUILD_TIMEOUT 600 重建超时,POSIX 用 SIGALRM,无 SIGALRM 的平台用守护线程看门狗
GRAPHIFY_FORCE 未设置 1/true/yes 时强制覆盖即使图变小
GRAPHIFY_OUT graphify-out 输出目录名;钩子还会读 <out>/.graphify_root 恢复真实仓库根
GRAPHIFY_REBUILD_LOG ~/.cache/graphify-rebuild.log 后台重建的输出日志
GRAPHIFY_MAX_WORKERS Windows/MSYS 下默认 1,其余为 CPU 核数 AST 抽取子进程数
GRAPHIFY_REBUILD_MEMORY_LIMIT_MB 未设置(不限制) RLIMIT_AS(Linux)/RLIMIT_DATA(macOS)限制重建内存;钩子还会先 os.nice(10) 降低优先级
GRAPHIFY_VIZ_NODE_LIMIT 未设置 可视化节点上限;可由项目 .graphifyrcviz_node_limit=<非负整数> 持久化,且以 ${GRAPHIFY_VIZ_NODE_LIMIT:-<n>} 形式烘焙进钩子,单次运行 git commit 时的显式赋值仍优先
PYTHONHASHSEED 钩子内固定为 0 保证聚类结果可复现

.graphifyrc 每行必须是 key=value 格式,status 命令会对已安装钩子里烘焙的 limit 与当前 .graphifyrc 值做比对,不一致时报告 "installed (out of date: ...)",提示重跑 graphify hook install

测试侧,tests/test_hooks.pytests/test_hook_guard.pytests/test_hook_out_of_project_paths.py 覆盖了钩子脚本生成、安装/卸载与越界路径拦截等行为。

三、原生 CLAUDE.md 集成

3.1 一次性接入

参考文档说明:对每个项目运行一次

graphify claude install

即让 graphify 在 Claude Code 会话中"always-on";对应实现是 install.py 的 claude_install。文档说它"向本地 CLAUDE.md 写入一个 ## graphify 小节",源码确认了这一点:目标文件是项目根CLAUDE.md(project_dir or Path(".")) / "CLAUDE.md"),使用 _replace_or_append_section 基于标记做"替换已有小节或追加",因此幂等——重复运行只会打印 "graphify already configured ... (no change)"。

但实现里还有一步文档未展开的动作:claude_install 末尾总是重新安装 Claude Code 的 PreToolUse 钩子_install_claude_hook),把旧的钩子载荷替换为当前版本(升级场景)。它会合并进 .claude/settings.jsonhooks.PreToolUse 数组,matcher 覆盖 Bash|Grep 搜索与 Read|Glob 读取两类工具调用(安装输出会打印 "PreToolUse hooks registered (Bash|Grep search + Read/Glob)")。项目级安装(project=True)会写入裸命令形式,因为此时 settings.json 会被提交进仓库,不应携带安装机器的绝对路径。

3.2 写入的 always-on 规则内容

写入 CLAUDE.md 的小节内容来自 always-on 片段 claude-md.md,其规则原文是:

  • 项目存在 graphify-out/ 知识图谱(god nodes、社区结构、跨文件关系);
  • 回答代码库问题时,若 graphify-out/graph.json 存在,先运行 graphify query "<question>";关系问题用 graphify path "<A>" "<B>",聚焦概念用 graphify explain "<concept>"——这些命令返回的是作用域受限的子图,通常远小于 GRAPH_REPORT.md 或原始 grep 输出;
  • 若存在 graphify-out/wiki/index.md,宽泛导航优先用它而不是直接翻源码;
  • 仅在宽泛架构评审或 query/path/explain 上下文不足时才读 GRAPH_REPORT.md
  • 修改代码后运行 graphify update . 保持图最新(纯 AST,无 API 成本)。

安装完成后的终端提示也印证了这一行为:"Claude Code will now check the knowledge graph before answering codebase questions and rebuild it after code changes."。若以 strict 模式安装,还会额外启用:每个会话第一次裸读文件会被拦截,直到先执行一次 graphify query(可用 GRAPHIFY_HOOK_STRICT=0 关闭)。

3.3 移除集成

graphify claude uninstall  # remove the section

对应实现 claude_uninstall 比"删掉小节"做得更彻底,它覆盖用户可能把配置挪到本地文件的几种变体:

  • CLAUDE.mdCLAUDE.local.md.claude/CLAUDE.local.md 三个位置精确切除 ## graphify 小节(按标记匹配,不会误伤用户自建的 ### graphify 之类标题);切除后文件若为空则直接删除该文件;
  • .claude/settings.json.claude/settings.local.json 两个文件里移除 graphify 的 PreToolUse 钩子项(用户可能把钩子挪到 local 文件避免提交进共享仓库);
  • 按作用域规则清理对应平台的 skill 树(项目级安装只删项目级,避免误删全局技能)。

四、要点小结与验证路径

  • 钩子不阻塞、不失败、可复现:提交后立即返回(Python 自研 detached launch)、解释器找不到时 exit 0PYTHONHASHSEED=0 固定聚类随机性——这三点共同解释了"图为什么不会挡住你提交,且每次重建结果稳定";
  • 增量是默认,全量是兜底:commit 走 changed_paths 增量重建并带锁队列合并;checkout 切分支走全量重建;
  • 第三方内容零破坏:追加式安装、标记块切除式卸载、.gitattributes 保留其他条目,都可用 hooks.py 中的 _install_hook/_uninstall_hook/_register_merge_driver 直接验证;
  • Claude 集成的实质:一条幂等的 ## graphify 规则段(先查图、改后重建)+ 一组 PreToolUse 钩子,移除时按项目/本地/全局三种作用域分别清理。

参考文档 hooks.md 面向的是 claw(AGENTS.md 系)平台的 skill 引用;由于 git 钩子与 CLAUDE.md 集成是平台无关/平台专用的独立命令,上述行为在 Claude Code 之外同样适用,graphify claude install/uninstall 则专用于 Claude Code。实际调试时,最直接的两个观察点是 ~/.cache/graphify-rebuild.log(每次后台重建的输出)与 git hooks 目录下的 post-commit/post-checkout 文件本身(标记块之间就是 graphify 注入的完整脚本)。

登录后查看全文
热门项目推荐
相关项目推荐