☰
husky Git 钩子疑难排错实战
2026/10/27 9:30:31 网站建设 项目流程

husky Git 钩子疑难排错实战

【免费下载链接】huskyGit hooks made easy 🐶 woof!项目地址: https://gitcode.com/gh_mirrors/hu/husky

跑git commit弹出husky - command not found in PATH=...,或钩子文件明明存在、提交却原样通过——这是 husky 排错里最高频的两类报错。本文不罗列问题,而是给一条诊断主线:基线检查 → 按core.hooksPath输出分叉 → 修复 → 回归验证,每步命令可直接复制执行。

🔍 基线检查:Git 版本、.git 与 hooksPath 三条命令

husky 9.x 不修改.git/hooks/下任何文件,安装时只执行一条git config core.hooksPath .husky/_,把钩子目录整体重定向到.husky/_(见 index.js)。所以一切排错都从这条配置读起。

先跑:

git --version

如果输出低于 2.9 →core.hooksPath是 Git 2.9 起才支持的配置项(docs/troubleshoot.md),钩子永远不会生效,先升级 Git,后面所有步骤都不必再看。

先跑:

ls -a

如果输出里没有.git→ 你不在 Git 仓库根目录,安装逻辑会直接返回.git can't be found(index.js),回到含.git的目录再继续。

先跑:

git config core.hooksPath

运行git config core.hooksPath,把输出记下来,下一站按它分叉。环境干净的判定标准:Git ≥ 2.9、.git存在、输出为.husky/_。官方测试正是用这条断言验证安装结果,见 test/functions.sh 中的expect_hooksPath_to_be ".husky/_"。

第一分叉:按 hooksPath 输出选择诊断方向

把上一条命令的输出对照下表,两个分支互斥:

git config core.hooksPath输出判断走向
空从未安装、被HUSKY=0跳过、或配置被其他工具覆盖分支①:重装
指向已删除的旧目录卸载残留,Git 把钩子重定向到不存在的路径分支①:清除残留
.husky/_安装正常,问题在钩子运行时分支②:查运行时

分支①:hooksPath 为空或指向别处,怎么重置

安装输出报错:三种文案对应三种环境

先跑:

npx husky

安装脚本的返回值本身就指明卡点(index.js):

  • 输出git command not found→ 执行安装的终端里没有 git。先跑command -v git,无输出 → 安装 Git 或把其路径加入 PATH。test/5_git_command_not_found.sh 用process.env.PATH = ''复现的正是这条分支
  • 输出.git can't be found→ 当前不在仓库根目录,回到含.git的目录重跑
  • 输出HUSKY=0 skip install→ 环境变量里带着HUSKY=0。运行unset HUSKY后重跑安装

原理一句话:安装 = 一条git config加一组写进.husky/_/的文件,三者缺一,core.hooksPath就不会被写入。

卸载后 .git/hooks/ 不工作:清除 hooksPath 残留

现象:删掉 husky 依赖后,手工放在.git/hooks/里的钩子也不再执行。

原因:卸载不会清理 Git 仓库配置,core.hooksPath仍指向已删除的.husky/_,Git 于是把钩子目录整体重定向到一个不存在的路径(docs/troubleshoot.md)。

修复:

git config --unset core.hooksPath

预期:再运行git config core.hooksPath无输出,Git 回到默认行为,使用.git/hooks/。怀疑有全局残留时,加--global前缀查看一次。

分支②:hooksPath 正确,钩子静默失效的两种原因

先建立心智模型:每次提交真正执行的,是统一运行时脚本.husky/_/h(即仓库根目录的 husky 脚本):它先 source 初始化文件、检查HUSKY、把node_modules/.bin注入 PATH,最后才以sh -e跑你的钩子。排查顺序因此是:环境变量 → 文件名。

HUSKY=0 静默禁用:检查 init.sh

运行时脚本在 source 完初始化文件之后、执行钩子之前,有一行判断(husky):

[ "${HUSKY-}" = "0" ] && exit 0

初始化文件是~/.config/husky/init.sh(Windows 为C:\Users\你的用户名\.config\husky\init.sh)。如果你曾为全局禁用或 CI 在其中写过export HUSKY=0,钩子会静默跳过、提交照常成功——表现为"钩子配好了却没跑"。

先跑:

echo "$HUSKY" cat ~/.config/husky/init.sh
  • 任一输出含HUSKY=0→ 删掉 init.sh 中该行,或临时unset HUSKY;长期禁用请从 init.sh 移除
  • test/9_husky_0.sh 验证了这条分支:在 init.sh 写入export HUSKY=0后,带exit 1钩子的git commit依然以 0 退出

顺带一提:如果终端出现husky - '~/.huskyrc' is DEPRECATED, please move your code to ~/.config/husky/init.sh,说明旧版~/.huskyrc还在,按提示迁移到 init.sh 即可。

钩子文件名不合法:对照标准清单

Git 只执行文件名恰好匹配标准钩子名的文件:precommit(缺连字符)和pre-commit.sh(带扩展名)都会被直接忽略(docs/troubleshoot.md)。

先跑:

ls .husky/
  • 任何文件名不在标准清单内 → 改名为标准名称并去掉扩展名
  • husky 安装时默认生成 14 个转发文件:pre-commit、commit-msg、post-commit、pre-push等,完整清单见 index.js

command not found 与 Windows tty 报错的专项修复

走到这里说明钩子确实在执行,报错出在脚本内部。

报错 command not found in PATH:GUI 客户端 + 版本管理器

husky - command not found in PATH=...只在钩子脚本以退出码 127 结束时打印(husky)。典型现象:终端里一切正常,IDE 或 Git GUI 客户端里报node: command not found。

原因:版本管理器(nvm、fnm、volta 等)靠 shell 启动文件把 Node 目录注入 PATH,而 GUI 启动的应用不经过启动文件,PATH 里没有 Node 安装路径(docs/how-to.md)。

修复:husky 运行时会先 source~/.config/husky/init.sh再跑钩子,把版本管理器的初始化代码复制进去:

# ~/.config/husky/init.sh export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

如果你的.zshrc足够轻量,也可以直接写. ~/.zshrc。

钩子里调用的命令不在 node_modules/.bin

运行时脚本已执行export PATH="node_modules/.bin:$PATH"(husky),钩子可以直接写eslint这类局部安装的命令;test/7_node_modules_path.sh 断言钩子内 PATH 必含node_modules/.bin。

  • 仍是 127 → 确认该命令真的在依赖里、node_modules已安装,必要时改用npm run <script>间接调用

Windows + Yarn 报 stdin is not a tty

Git Bash 下钩子环境没有交互式 tty,Yarn 的交互命令会检测失败(docs/troubleshoot.md)。

修复:创建.husky/common.sh:

command_exists () { command -v "$1" >/dev/null 2>&1 } # Workaround for Windows 10, Git Bash, and Yarn if command_exists winpty && test -t 1; then exec < /dev/tty fi

在每条执行 yarn 的钩子顶部 source 它:

# .husky/pre-commit . .husky/common.sh yarn ...

原理:有winpty且 stdout 是终端时,把 stdin 重定向到/dev/tty,yarn 就能拿到真终端。

回归验证:用一条必失败钩子确认链路

仓库自己的测试(test/6_command_not_found.sh)就是标准做法——写一条必失败的钩子,验证调用链连通:

echo "exit 127" > .husky/pre-commit git commit -m foo

预期输出:提交被拦截,终端出现husky - command not found in PATH=...。看到它 → 链路正常,问题只可能在你的钩子脚本内容里;完全没反应 → 回到分支①重新确认 hooksPath。

再换成成功场景收尾:

echo 'echo "$PATH" | grep "node_modules/.bin"' > .husky/pre-commit git commit -m foo

提交成功 → PATH 注入正常。最后确认一次配置:

git config core.hooksPath

输出.husky/_即为最终态。记住口诀:先看 Git 2.9,再读 hooksPath——空则重装,对则查HUSKY=0,最后用一条必失败钩子验链路。

【免费下载链接】huskyGit hooks made easy 🐶 woof!项目地址: https://gitcode.com/gh_mirrors/hu/husky

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询