在 pre-commit 钩子中应使用 git status --porcelain | grep '^[m?] composer.lock$' 判断 composer.lock 是否被修改但未暂存,因其能准确捕获未暂存修改或未跟踪的新 lock 文件,而 git diff --quiet 会漏检;需先切至仓库根目录、显式指定 composer 可执行文件并检查 composer.json 存在性;同时注意权限、换行符、多模块等常见陷阱。

git pre-commit 钩子里怎么判断 composer.lock 被改了但没 git add
直接看工作区和暂存区差异就行,git status --porcelain 是最稳的判断依据。它输出格式固定,M 开头表示已修改未暂存,?? 表示未跟踪——这两种情况都该拦住。
-
git status --porcelain | grep '^[M?] composer.lock$'能捕获未暂存修改或未跟踪的新 lock 文件 - 别用
git diff --quiet composer.lock,它只对比暂存区和 HEAD,漏掉“改了但没git add”的场景 - 如果项目用
composer install --no-interaction自动更新 lock,钩子得在install后立刻检查,否则 CI 会因 lock 不一致失败
为什么不能只靠 composer install 的退出码判断 lock 是否同步
composer install 默认不生成或更新 composer.lock,只校验现有 lock 是否匹配 composer.json。即使 lock 已过期(比如 composer.json 增加了依赖但 lock 没更新),它也静默成功,退出码仍是 0。
- 真正能暴露不一致的是
composer update --dry-run:如果有差异,它会输出要变更的包,并返回非 0 码 - 但
--dry-run很慢,尤其在 CI 上;预提交钩子里更推荐用git status快速拦截 - 例外:当团队约定“所有变更必须先
composer update再改composer.json”,这时才适合在钩子里跑composer update --dry-run
pre-commit 钩子脚本里怎么安全调用 Composer
钩子执行路径不一定在项目根目录,composer 命令可能找不到 composer.json,也可能误用全局 Composer 版本。
- 用
cd $(git rev-parse --show-toplevel)切到仓库根,再执行后续命令 - 显式指定 Composer 可执行文件:
php ./composer.phar install --no-interaction(如果项目自带 phar)或./vendor/bin/composer install - 避免用
which composer,CI 环境或 Docker 容器里常没有全局安装的 Composer - 加个
test -f composer.json防御性检查,不是所有子目录都需要跑这个钩子
Git hooks + Composer 的典型失败场景
最常见的不是脚本写错,而是权限和路径错位。比如 macOS 上钩子文件没 x 权限,或 Windows 用户用了 CRLF 换行导致 #!/usr/bin/env bash 解析失败。
- 用
chmod +x .git/hooks/pre-commit确保可执行 - 钩子里别写
composer update自动修复——这会让提交行为不可预测,且可能引入意料外的依赖升级 - 如果项目同时用
pnpm或yarn管理前端依赖,注意它们生成的pnpm-lock.yaml或yarn.lock也要一并检查,逻辑不能只盯composer.lock
真正麻烦的是多层嵌套子模块(git submodule)里的 Composer 项目——钩子默认不递归触发,得手动遍历 .gitmodules,这点很容易被忽略。










