首页
/ graphify 的 Git Hook 与 CLAUDE.md 常驻集成:让知识图谱随提交自动保鲜

graphify 的 Git Hook 与 CLAUDE.md 常驻集成:让知识图谱随提交自动保鲜

2026-09-06 14:48:27作者:彭桢灵Jeremy

本文围绕 graphify/skills/copilot/references/hooks.md 这一参考文档展开,讲清 graphify 的两条"常驻化"集成路径:一是通过 graphify hook 安装 git 钩子,让知识图谱在每次 commit(以及分支切换)后自动增量重建;二是通过 graphify claude 把使用规则写进项目 CLAUDE.md,让 Claude Code 会话无需手动触发命令即可始终"先查图、再答问题"。读完后你将掌握两套命令的完整用法、钩子脚本的触发条件与跳过逻辑、跨平台 Python 解释器探测原理,以及 .graphifyrc 等配套配置项,并能在自己的仓库中验证安装状态。

一、两条集成路径解决什么问题

graphify 把代码库、文档、SQL schema 与配置文件解析成可查询的知识图谱,产物默认落在 graphify-out/ 目录(graph.jsonGRAPH_REPORT.md 等)。问题在于:代码一直在变,图谱会过期。hooks.md 文档给出的答案是两种互补机制:

  1. git commit hook:安装一个 post-commit 钩子,在每次提交后自动重建图谱。文档原文强调其特点——"No background process needed - triggers once per commit, works with any editor"(无需常驻后台进程,每次提交触发一次,适用于任何编辑器)。
  2. 原生 CLAUDE.md 集成:每个项目运行一次 graphify claude install,把使用 graphify 的规则写进本地 CLAUDE.md,让 Claude Code 在未来所有会话中都"先查图谱再回答",后续无需再手动输入 /graphify

下面分别深入。

二、Git commit hook:命令与行为

2.1 三个基本命令

文档给出的操作面非常小,只有三条子命令:

graphify hook install    # 安装
graphify hook uninstall  # 卸载
graphify hook status     # 查看状态

CLI 侧的分发逻辑在 cli.pygraphify hook 子命令被路由到 hooks.py 中的 install / uninstall / status 三个函数;传错子命令会打印 Usage: graphify hook [install|uninstall|status] 并以退出码 1 结束。

2.2 每次提交后实际发生了什么

文档描述的流程是:每次 git commit 之后,钩子通过 git diff HEAD~1 检测哪些代码文件发生了变化,仅对这些文件重跑 AST 抽取,然后重建 graph.jsonGRAPH_REPORT.md;文档和图片类的变更会被钩子忽略——这类变更需要手动运行 /graphify --update

对照 hooks.py 中生成的 post-commit 脚本模板,可以看到完整的判定链(任何一步命中就静默退出、不做重建):

  1. rebase/merge/cherry-pick 期间跳过:检查 $GIT_DIR 下的 rebase-mergerebase-applyMERGE_HEADCHERRY_PICK_HEAD,避免阻塞 git rebase --continue。源码注释还特意指出优先读取 git 导出的 GIT_DIR,手动执行时才回落到 git rev-parse,因为"在装有杀毒软件的 Windows 机器上每次 git 执行开销超过 1 秒"。
  2. GRAPHIFY_SKIP_HOOK=1 手动跳过:环境变量一票否决。
  3. linked worktree 跳过(worktree guard):通过比较 git rev-parse --git-dir--git-common-dir(都先 cd 解析成绝对路径)判断当前是否处于 git worktree add 出来的附属工作树;是则直接退出,避免在主检出的 graphify-out/ 上产生无主增量图并与 CI 的 git clean 竞态。
  4. 取变更列表CHANGED=$(git diff --name-only HEAD~1 HEAD ...),为空则退出;若变更只有 graphify-out/ 产物(图谱输出被纳入了版本控制时),也直接退出,避免"重建产物又触发重建"的循环。
  5. 探测可用的 Python 解释器(下一节详述),然后把变更列表通过 GRAPHIFY_CHANGED 环境变量传给一个脱离 git 进程的后台 Python 任务。

后台任务的核心在 hooks.py 内嵌的 Python 片段:它从 GRAPHIFY_CHANGED 解析出路径列表,调用 graphify.watch._rebuild_code(root, changed_paths=changed, force=...) 做增量重建,随后 best-effort 刷新工作记忆(graphify.reflect)。几个可配置点值得注意:

  • GRAPHIFY_REBUILD_TIMEOUT:重建超时(秒),默认 600。POSIX 下用 SIGALRM 实现,无 SIGALRM 的平台(Windows)退回 threading.Timer 看门狗。
  • GRAPHIFY_FORCE:置为 1/true/yes 时强制全量重建。
  • GRAPHIFY_OUT:输出目录名,默认 graphify-out;若该目录下存在 .graphify_root 标记文件,则以其中记录的路径作为重建根。
  • PYTHONHASHSEED=0:脚本开头显式导出。源码注释解释了原因——networkx 的 louvain 聚类迭代的字符串键集合顺序受 hash 随机化影响,固定种子才能让 graphify-out 的输出跨次运行可复现。

2.3 跨平台 Python 解释器探测:钩子最"重"的部分

graphify 推荐用 uv tool install 安装,此时解释器藏在隔离 venv 里,PATH 上的只是启动器;而 GUI git 客户端和 CI runner 的 PATH 常常精简到找不到 ~/.local/bin。因此安装钩子时会把执行 graphify hook install 的解释器绝对路径钉死进脚本(_PINNED_PYTHON__ 占位符在 hooks.py_pinned_python() 中替换,仅当路径通过字符白名单校验——不含 $、反引号、引号等 shell 元字符——才允许写入,防止生成脚本被注入)。

钩子运行时按优先级逐级探测(hooks.py_PYTHON_DETECT 模板):

优先级 探测方式 覆盖场景
1 安装时钉死的 _PINNED 解释器 最常见路径,GUI 客户端/CI 下依然有效
2 读取 graphify-out/.graphify_python 文件 uv tool 重装存活;读取值同样走字符白名单
3 PATH 上的 graphify 启动器:Windows 布局下尝试 Scripts 目录相邻的 python.exe;POSIX 布局下解析 shebang(head -c 256 + tr -d '\000' 先剥 NUL,且要求首行以 #! 开头,防止把无 shebang 的二进制 trampoline 的字节读进 shell) uv tool 在 Windows 上是二进制启动器,无 shebang 可解析
4 扫描 uv tool 环境目录:${UV_TOOL_DIR}~/.local/share/uv/tools~/AppData/Roaming/uv/tools 下的 */bin/python*/Scripts/python.exe 前三级全部落空时的 uv 兜底;只有其 python 能 import graphify 才采用
5 python3 / python 系统或 venv 直装

每一级都用 importlib.util.find_spec('graphify') 探测可用性而非真正 import——源码注释说明这是性能教训:整体 import 会触发整个包加载(在 AV 扫描的大 site-packages 机器上冷启动 10 秒以上),且旧版本曾同步探测多达四次,"卡住每次 commit"。全部失败时钩子大声报错并以退出码 0 结束(不阻塞 commit),提示"把 graphify 的 bin 目录加入 PATH 或在正确的环境里重跑 graphify hook install"。

2.4 为什么不用 nohup &:脱离启动器

大仓库的全量重建可能非常耗时,post-commit 钩子必须立刻返回。旧实现用 nohup ... & 后台化,但 Git for Windows 自带的 MSYS shell 没有 nohup(也没有 setsid),该行直接失败且 git 仍返回 0——图谱静默过期。现在的方案(hooks.py_LAUNCHER_TEMPLATE)是让 Python 自己做脱离:外层小进程用 subprocess.Popen 拉起真正的重建任务,POSIX 用 start_new_session=True(等价 setsid),Windows 用 CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP(并尝试附加 CREATE_BREAKAWAY_FROM_JOB)。注释里还记录了一个 Windows 11 细节:不能把 CREATE_NO_WINDOW "简化"回 DETACHED_PROCESS,否则每次 commit 都会闪出一个空 Terminal 窗口。

重建子进程的输出统一追加到 ~/.cache/graphify-rebuild.log(可用 GRAPHIFY_REBUILD_LOG 覆盖);commit 时终端会打印一行提示:

[graphify hook] launching background rebuild (log: ~/.cache/graphify-rebuild.log)

这是排查"钩子装了但图谱没更新"的第一现场。

2.5 附带的 post-checkout 钩子与合并驱动

graphify hook install 除了 post-commit 还会安装 post-checkout 钩子hooks.py):切换分支且新旧 HEAD 不同、graphify-out/ 已存在时,触发一次全量重建(分支切换可能触及任意文件,增量路径不适用;_rebuild_code 内部的 flock 防止 commit 与 checkout 背靠背触发时排队堆积)。它与 post-commit 共享同一套跳过逻辑(rebase/merge、GRAPHIFY_SKIP_HOOK、worktree guard)。

此外,install 还会注册 git 合并驱动(hooks.py_register_merge_driver):

  • 写入 git configmerge.graphify.namemerge.graphify.driver(解释器路径同样钉死并双引号包裹,兼容用户名含空格的 Windows 路径);
  • .gitattributes 追加一行 <输出目录>/graph.json merge=graphify,实现 graph.json 的 union 合并;已有条目会保留,重复安装幂等。

graphify hook uninstall 对称地移除两个钩子的 graphify 区段(按 # graphify-hook-start/end 等标记精确切除,其余钩子内容保留)、卸载合并驱动的两条 config 与 .gitattributes 行。

2.6 共存、幂等与配置

文档特别承诺"If a post-commit hook already exists, graphify appends to it rather than replacing it"(已有 post-commit 钩子时是追加而非替换)。源码 hooks.py_install_hook 落实为三种情况:钩子文件不存在则创建(chmod 0o755);存在且含 graphify 标记则原地更新该区段;存在但无标记则追加到文件末尾。重复安装同一版本时返回 already installed at ...

状态查询 graphify hook status 输出形如:

post-commit: installed
post-checkout: installed
merge driver: registered (graphify-out/graph.json merge=graphify)
viz node limit: 3000

它还承担"配置漂移检测":若 .graphifyrc 里的 viz_node_limit 与已写入钩子的烘焙值不一致,会提示 installed (out of date: ...)

.graphifyrc(仓库根目录)目前支持 viz_node_limit(非负整数)配置,解析逻辑在 hooks.py。安装时它被烘焙成 export GRAPHIFY_VIZ_NODE_LIMIT="${GRAPHIFY_VIZ_NODE_LIMIT:-<n>}"——:- 形式保证每次提交前手动 GRAPHIFY_VIZ_NODE_LIMIT=... git commit 的临时覆盖优先于项目默认值。

2.7 环境参数速查

变量 作用 默认
GRAPHIFY_SKIP_HOOK 1 时 commit 与 checkout 钩子都跳过 未设置
GRAPHIFY_REBUILD_TIMEOUT 后台重建超时(秒),<=0 关闭 600
GRAPHIFY_FORCE 1/true/yes 时强制全量重建 未设置
GRAPHIFY_OUT 输出目录名 graphify-out
GRAPHIFY_REBUILD_LOG 重建日志路径 ~/.cache/graphify-rebuild.log
GRAPHIFY_VIZ_NODE_LIMIT 可视化节点上限(项目默认来自 .graphifyrc
GRAPHIFY_MAX_WORKERS 并行 worker 数;Windows/MSYS 下钩子默认强制为 1(避免 GUI 客户端继承的脆弱管道句柄),显式设置则覆盖 平台相关
UV_TOOL_DIR 覆盖 uv tool 环境扫描位置 平台默认

测试侧对以上行为有大量断言,见 tests/test_hooks.py:幂等安装、追加到已有钩子、钉死解释器路径、不含 nohup、跨平台脱离启动、worktree 守卫、.graphifyrc 解析与覆盖优先级、合并驱动注册/卸载等均有独立用例;tests/test_hook_guard.pytests/test_read_hook.py 则覆盖 agent 端钩子的行为。

三、CLAUDE.md 原生集成:让 Claude Code 常驻使用图谱

3.1 安装与写入的内容

文档给出的命令是:

graphify claude install

其效果是把一个 ## graphify 区段写进项目本地 CLAUDE.md,"instructs Claude to check the graph before answering codebase questions and rebuild it after code changes"。卸载用:

graphify claude uninstall  # 移除该区段

实现位于 install.pyclaude_install(),两个关键设计:

  • 幂等的区段管理:通过 _replace_or_append_sectioninstall.py)以 ## graphify 为标记——已存在该区段就原地替换,不存在就追加到文件末尾,不动其他内容。内容完全相同则打印 graphify already configured in ... (no change)
  • 区段内容来自打包文件:写入的是 graphify/always_on/claude-md.md 的逐字内容,实际注入的规则是:
## graphify

This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.

Rules:
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).

即一套明确的使用次序:代码库问题先 graphify query(关系用 graphify path,概念用 graphify explain,返回的是远小于全量报告的子图)→ 有 wiki 索引时用它做广度导航 → GRAPH_REPORT.md 只用于宏观架构审查 → 改完代码跑 graphify update .(纯 AST,无 API 开销)保持图谱新鲜。

3.2 顺带安装的 PreToolUse 钩子

claude_install() 在写完 CLAUDE.md总是再装一次 Claude Code 的 PreToolUse 钩子(install.py_install_claude_hook):在 .claude/settings.jsonhooks.PreToolUse 数组中先剔除旧的 graphify 条目(matcher 为 Bash|Grep/Glob|Grep/Read|Glob 且含 "graphify" 字样),再写入新载荷,最后带备份写回。注释说明了为什么"每次重装"——为了让升级时旧版提示词(如 issue #580 之前的措辞)被替换。

两个可选行为值得了解:

  • 项目级安装--project 模式下写的是裸命令 graphify ... 而不是本机绝对路径,因为此时 .claude/settings.json 会被提交进共享仓库,写死安装机器的路径对协作者是错的(对应 issue #3129 的注释)。
  • strict 模式:开启后每个会话第一次"绕过图谱直接读原始文件"的行为会被钩子拦截,直到先执行过一次 graphify query;也可用 GRAPHIFY_HOOK_STRICT=0 关闭(解析逻辑见 cli.py,环境变量优先于安装时烘焙的标记)。

claude_uninstallinstall.py 起)对称地移除:删除 CLAUDE.md(及其 local-only 变体)中的 ## graphify 区段——删空后整个文件会被删除;同时清理 settings.jsonsettings.local.json 中的 graphify PreToolUse 条目(用户可能把钩子挪到 settings.local.json 以免提交,见 issue #1731 注释)。

四、落地检查清单

  • graphify hook status:确认 post-commit / post-checkout / merge driver 三行均为 installed/registered,且无 out of date 提示;
  • commit 一次代码变更,观察终端打印 launching background rebuild,随后查看 ~/.cache/graphify-rebuild.log 与刷新后的 graphify-out/graph.jsonGRAPH_REPORT.md
  • graphify-out/.graphify_python 与钩子脚本内的 _PINNED 路径是否指向一个真正能 import graphify 的解释器(GUI 客户端/CI 环境重点检查);
  • 文档、图片变更不会触发钩子重建,需要时手动 /graphify --update

两条路径的定位不同:git 钩子解决"图谱数据的新鲜度"(无人值守、纯 AST、零 API 成本),CLAUDE.md 集成解决"使用行为的新鲜度"(让 agent 每次都走 query/path/explain 这条最短路径)。二者叠加,项目里的知识图谱就能做到"提交即更新、提问即命中"。

参考文件

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