pre-commit不生效需检查.git/hooks/pre-commit权限与shebang;推荐编译go工具为二进制调用;gofmt/go vet/staticcheck应显式控制参数;避免go test全量运行和go mod tidy自动修改,统一环境版本确保一致性。

pre-commit 命令不生效,检查 .git/hooks/pre-commit 文件权限和内容
Git 钩子脚本必须有可执行权限,且第一行得是有效的 shebang(比如 #!/bin/bash)。常见错误是直接写了个 Go 程序但没编译,或用 go run 写在钩子里却没处理好 GOPATH / Go modules 环境。
- 确保
.git/hooks/pre-commit是普通文件(不是符号链接),且运行chmod +x .git/hooks/pre-commit - 避免在钩子里直接调用
go run main.go:CI 和不同开发者本地环境的 Go 版本、module 模式可能不一致,容易报no required module provides package - 推荐做法:用
go build -o ./bin/precommit ./cmd/precommit编译成二进制,钩子里只执行./bin/precommit - 如果用
pre-commit.com框架,注意它默认不支持 Go 项目原生集成——它依赖 Python 环境,pre-commit install生成的是 Python 包装器,和 Go 工具链是隔离的
用 gofmt + go vet + staticcheck 做基础校验
这些工具覆盖了格式、语法隐患、静态缺陷三类高频问题,但默认行为和 CI 要求常有偏差,需显式控制参数。
-
gofmt -l -w .:-l列出未格式化文件,-w写入修改;省略-w时仅检查,适合 pre-commit 只报错不修 -
go vet -tags=unit ./...:加上-tags避免因构建标签跳过部分包导致漏检 -
staticcheck -go=1.21 ./...:明确指定 Go 版本,防止团队里有人用旧版 Go 导致检查规则不一致 - 注意
go vet在 Go 1.22+ 默认启用更多检查项,若团队还没升级,建议锁死版本(如GOEXPERIMENT=nogotypes)或统一go版本
避免在 pre-commit 中运行 go test 全量用例
本地提交时跑全部单元测试会严重拖慢体验,尤其当测试含 HTTP client、DB 连接或 sleep 逻辑时,容易误判为“钩子卡死”。
- 只对本次改动的文件做测试:
go test $(git diff --cached --name-only | grep '\.go$' | xargs -r dirname | sort -u | xargs -r) - 跳过标记为
//go:build integration的测试:加-tags=unit参数 - 设超时:
go test -timeout=30s,避免某个测试 hang 住整个提交流程 - 更稳妥的做法是把
go test移到 CI 阶段,pre-commit 只做 lint / format / vet 这类毫秒级检查
Go module 校验缺失导致 go mod tidy 提交污染
很多人把 go mod tidy 加进 pre-commit,结果每次提交都自动改 go.mod 和 go.sum,造成无关变更,还可能因 GOPROXY 差异引入不一致依赖。
立即学习“go语言免费学习笔记(深入)”;
- 只在校验阶段运行:
go mod tidy -dry-run,它会输出差异但不写文件;非零退出码表示需要 tidy - 配合
git status --porcelain检查是否有未暂存的go.mod或go.sum变更,有则拒绝提交 - 如果项目强制要求
go.mod必须最新,建议在 CI 中跑go mod tidy && git diff --exit-code go.mod go.sum,而不是塞进 pre-commit - 注意
go mod tidy在 Go 1.21+ 会自动添加//indirect注释,老版本不会,混用会导致反复变更
真正难的不是加几个命令,而是让每个检查项在所有开发机上表现一致——Go 版本、GOPROXY、GOSUMDB、甚至 shell 类型(bash/zsh)都可能影响命令展开和退出码。别指望一次配完就一劳永逸。










