规则挂在钩子上,一个词就绕过去了
目标
创建一组随仓库共享的客户端钩子,并亲自确认它能阻止什么、阻止不了什么。
为什么重要
钩子常被介绍为“把规则自动化”,但客户端钩子实际做的事是快速反馈,而不是强制。不懂这一点,只把规则挂在客户端,就会在自以为规则得到遵守的状态下持续很久。本实验会亲手打破这种信念——创建三条规则,而一个参数就能让它们全部通过。并且通过构造只有索引中含有机密的状态,来确认为什么 pre-commit 读取工作树是不行的。被提交的是索引而不是工作树,这个事实即使不使用钩子也值得了解。
步骤
- 创建
/root/gitx9/repo,并把core.hooksPath指定为.githooks。 - 用
.githooks/commit-msg强制标题格式,并把被拒绝的记录保存到notes/reject.txt。 - 用
.githooks/pre-commit阻止超过 100000 字节的文件,并保存到notes/bigfile.txt。 - 把该钩子改为查看索引,阻止以
AKIA开头的字符串,并把只看工作树为什么会被绕过的原因保存到notes/index.txt。 - 用
--no-verify创建一个违反规则的提交,并保存到notes/bypass.txt。 - clone 出
/root/gitx9/clone,把哪些内容会随之带过来、哪些不会,保存到notes/share.txt。 - 创建
/root/gitx9/origin.git,用.githooks/pre-push阻止直接向mainpush,并保存到notes/prepush.txt。 - 在
notes/report.md中总结客户端钩子和服务器钩子各自的作用。
参考
- 钩子文件必须赋予执行权限(
chmod +x)。没有的话,git 会悄悄跳过。 - 这个镜像里没有 python3。钩子请用
sh、git、grep、awk编写。 - 第 4 步的测试字符串是
AKIAIOSFODNN7EXAMPLE。匹配模式请取AKIA后跟 16 位大写字母和数字。 - 常见错误:创建了钩子却不提交。只有放进仓库,才能共享。
- 常见错误:在第 7 步最终把
mainpush 了出去。应当确认它被阻止,只推送功能分支。
决定把钩子放进仓库里
创建 /root/gitx9/repo,并把 core.hooksPath 指定为 .githooks。
.git/hooks/ 不会随 clone 带过来。用 git config core.hooksPath .githooks 把查找钩子的位置移到仓库所跟踪的目录。目录也要事先创建好。每个仓库还必须指定 user.email 和 user.name 才能提交。
强制标题格式
用 .githooks/commit-msg 强制标题格式,并把被拒绝的记录保存到 notes/reject.txt。
commit-msg 钩子以 $1 接收消息文件的路径。只看第一行,用 grep -qE 检查是否符合 <타입>(<범위>): <설명>(占位符依次为类型、范围、说明)的格式。类型取 feat、fix、docs、refactor、test、chore、build 之类即可。不符合时,要向标准错误说明哪里不对,并以非 0 值退出。别忘了 chmod +x。
在提交之前阻止大文件
用 .githooks/pre-commit 阻止超过 100000 字节的文件,并保存到 notes/bigfile.txt。
pre-commit 不接收参数。哪些内容即将被提交,必须自己用 git diff --cached --name-only --diff-filter=ACM 查出来。大小超过 100000 时,说明是哪个文件、多少字节,并以非 0 值退出。亲自确认被阻止之后,要把那个大文件从暂存区中取下。
被提交的是索引,而不是工作树
把该钩子改为查看索引,阻止以 AKIA 开头的字符串,并把只看工作树为什么会被绕过的原因保存到 notes/index.txt。
进入索引的 blob 是 git ls-files -s -- <경로>(占位符为路径)输出的第二列,内容用 git cat-file -p <blob> 或 git show :<경로> 读取。大小也用 git cat-file -s <blob> 测量。然后在文件中写入 AKIAIOSFODNN7EXAMPLE 并 git add,再只把工作树一侧的文件改干净,然后提交。如果钩子读取的是工作树,这个提交本来会原样通过。
一个参数就能全部通过
用 --no-verify 创建一个违反规则的提交,并保存到 notes/bypass.txt。
git commit --no-verify 会跳过 pre-commit 和 commit-msg。请创建一个违反标题规则的提交并留在历史中。然后一并写下:这不是缺陷而是设计,所以需要强制力的规则应当挂在哪里。
钩子文件会随 clone 带过来,配置不会
clone 出 /root/gitx9/clone,把哪些内容会随之带过来、哪些不会,保存到 notes/share.txt。
执行 git clone /root/gitx9/repo /root/gitx9/clone,并在其中查看三件事——.githooks/ 里有什么、git config --local core.hooksPath 输出什么、.git/hooks/ 里有什么。确认之后,在 clone 中也打开 core.hooksPath。
试着在客户端阻止受保护分支
创建 /root/gitx9/origin.git,用 .githooks/pre-push 阻止直接向 main push,并保存到 notes/prepush.txt。
pre-push 通过标准输入逐行接收 로컬참조 로컬해시 원격참조 원격해시(占位符依次为本地引用、本地哈希、远程引用、远程哈希)。只要有一行的远程引用(원격참조)是 refs/heads/main,就阻止它。远程仓库用 git init --bare /root/gitx9/origin.git 创建,再用 git remote add origin 挂上。确认 main 的 push 被阻止之后,只推送功能分支。
哪里负责通知,哪里负责阻止
在 notes/report.md 中总结客户端钩子和服务器钩子各自的作用。
一张表加几行规则就够了。务必写下客户端钩子挡不住的三种情况,并以此为由,说明为什么要把同一条规则挂在两个地方。另外,“钩子太慢的话,大家就会养成绕过的习惯”这条实际工作中的建议也很有价值。