Clone 之后 git hooks 不执行

团队配置了 pre-commit、commit-msg 等 git hooks,但新成员 clone 后这些 hooks 从不触发,代码质量检查和格式化完全失效。本文解释为什么 hooks 不随 clone 传播,并给出正确的团队 hook 分发方案。

你的团队在 .git/hooks/pre-commit 里配置了 ESLint 检查,commit-msg hook 验证 commit 消息格式,但新同事 clone 仓库后提交代码,这些检查完全没有触发。ESLint 错误的代码被直接提交进了主分支。原因其实很简单:.git/ 目录不被 Git 追踪,不会随 clone 或 push 传播,每个人 clone 后都有一个全新的空 hooks 目录。这是 Git 的设计,安全考量是避免克隆操作自动执行不受信任的代码。

常见原因

1. .git/hooks/ 不在版本控制中

根本原因。Git 故意不追踪 .git/ 目录,包括 hooks。就算你把 hook 脚本放进 .git/hooks/,推送后其他人 clone 也不会得到这些文件。

怎么判断ls .git/hooks/ 查看本机 hooks,再让新同事执行同样命令,若结果完全不同(或新同事只有 .sample 文件),说明 hooks 没有被分发。

2. hooks 目录已配置但脚本没有可执行权限

即使通过某种方式把 hook 脚本分发到了 .git/hooks/,文件权限不是可执行(chmod +x)的话 Git 也不会执行,静默跳过。

怎么判断ls -la .git/hooks/pre-commit,若权限是 -rw-r--r-- 而非 -rwxr-xr-x,缺少执行权限。

3. core.hooksPath 未配置或配置路径不存在

Git 2.9+ 支持 core.hooksPath 把 hooks 目录指向仓库内的某个路径(如 .githooks/),该目录可以被版本控制。但配置需要每个开发者在本地执行 git config core.hooksPath .githooks,若 setup 步骤被遗漏,配置就没有生效。

怎么判断git config core.hooksPath 若无输出,说明 hooksPath 没有被设置,Git 仍然使用默认的 .git/hooks/

4. 使用了 Husky 但 prepare 脚本没有自动运行

Husky 是流行的 hook 管理工具,在 package.json 里配置 "prepare": "husky install" 后,执行 npm install 时会自动安装 hooks。但若新同事直接 clone 后没有执行 npm install(比如只用 yarn,或者项目用 pnpm),prepare 脚本不会运行,hooks 就不存在。

怎么判断cat package.json | grep prepare,若有 "prepare": "husky install".husky/ 目录里的 hook 没有被执行,检查是否运行过 npm install

5. Windows 与 Unix 换行符导致 hook 脚本无法执行

团队在 macOS/Linux 上编写的 hook 脚本被 Windows 开发者 clone 后,CRLF 自动转换把脚本第一行的 #!/bin/bash 变成了 #!/bin/bash\r,shell 无法识别这个 shebang,脚本报错后 Git 跳过 hook 执行。

怎么判断file .git/hooks/pre-commit 若输出包含「CRLF line terminators」,就是这个问题。

6. --no-verify 被写进了团队脚本或 IDE 配置

某个开发者为了绕过一次慢速 hook,在 IDE 的 Git commit 设置里勾选了「Skip pre-commit hooks」或者在 commit 命令里加了 --no-verify,并且这个设置被同步到了共享配置,其他人也受影响。

怎么判断:检查 VS Code 的 settings.json(搜索 git.enableCommitSigning)或 IntelliJ 的 VCS 设置,是否有跳过 hooks 的选项被开启。

最短修复路径

Step 1:将 hook 脚本移到版本控制目录

mkdir -p .githooks
cp .git/hooks/pre-commit .githooks/pre-commit
chmod +x .githooks/pre-commit
git add .githooks/
git commit -m "chore: add team git hooks to .githooks/"

Step 2:在仓库里统一配置 hooksPath

git config --local core.hooksPath .githooks

但这只对你本机生效。需要让所有人在 clone 后也执行这个命令。

Step 3:通过 setup 脚本自动化

在项目根目录创建 scripts/setup.sh

#!/bin/sh
git config core.hooksPath .githooks
echo "Git hooks configured."

并在 README.mdCONTRIBUTING.md 里注明 clone 后必须执行 ./scripts/setup.sh

Step 4:若使用 npm 生态,改用 Husky 自动化

npm install --save-dev husky
npx husky init
# 这会在 package.json 里添加 "prepare": "husky"
# 并创建 .husky/ 目录

之后 npm install 会自动安装 hooks,无需手动步骤。

Step 5:修复 Windows CRLF 问题

.gitattributes 里为 hook 脚本强制 LF:

.githooks/* text eol=lf

然后重新 checkout:

git rm --cached .githooks/*
git checkout .githooks/

预防建议

  • 采用 Husky + prepare 脚本的方案,是目前最可靠的 hook 自动分发方式,npm/yarn/pnpm install 后自动生效,无需记住手动步骤。
  • README.md 顶部显著位置写明 setup 步骤,并在 CI 里增加 hook 配置检查,若 core.hooksPath 未设置则发出警告(不阻断,只告知)。
  • hook 脚本保持轻量,pre-commit 检查控制在 3 秒内,避免开发者因为慢而用 --no-verify 绕过。
  • .gitattributes 里为所有 shell 脚本设置 text eol=lf,防止 Windows 上的 CRLF 问题:*.sh text eol=lf
  • 团队的 onboarding checklist 里加一条「执行 ./scripts/setup.shnpm install」,并由导师在入职第一天检查确认。
  • 定期在 CI 里跑 hook 脚本做同等检查(如 ESLint、单元测试),即使本地 hook 被绕过,CI 也能兜底。

常见问答 (FAQ)

Q: core.hooksPath 可以配置在全局 git config 里,让所有仓库都用吗? A: 可以,git config --global core.hooksPath ~/.githooks,但这会把所有仓库的 hooks 都指向同一个目录,可能产生混乱。推荐每个仓库独立配置,或者通过 includeIf 按目录区分配置。

Q: 仓库用了 Git LFS,会影响 hooks 的执行吗? A: 不会直接影响,但 git lfs install 会在 .git/hooks/ 里安装自己的 LFS 钩子(post-checkoutpre-push 等)。若使用 core.hooksPath 把 hooks 目录移走,LFS 的钩子也需要手动迁移到新目录,否则 LFS 功能会失效。

Q: 我能在 hook 里调用 Docker 运行检查,不依赖本地环境安装吗? A: 可以,pre-commit: docker run --rm -v $(pwd):/app myteam/linter:latest 在容器里运行检查,不依赖本地工具版本。代价是首次启动需要拉取镜像,速度较慢;可以在 CI 里用同一个镜像保证一致性。

Q: commit-msg hook 报错但提交还是成功了,是 hook 没运行吗? A: 若 hook 脚本的退出码是 0,Git 认为检查通过,不管脚本里是否打印了错误信息。确保 hook 脚本在失败时返回非零退出码:exit 1。可以用 bash -x .git/hooks/commit-msg "test-message" 调试。

相关阅读

标签: #git #version-control #排查