TT Lab
开始
学习 学习路径 课程

Git 实战

规则挂在钩子上,一个词就绕过去了

在 TT Lab 中继续学习

目标

创建一组随仓库共享的客户端钩子,并亲自确认它能阻止什么、阻止不了什么。

为什么重要

钩子常被介绍为“把规则自动化”,但客户端钩子实际做的事是快速反馈,而不是强制。不懂这一点,只把规则挂在客户端,就会在自以为规则得到遵守的状态下持续很久。本实验会亲手打破这种信念——创建三条规则,而一个参数就能让它们全部通过。并且通过构造只有索引中含有机密的状态,来确认为什么 pre-commit 读取工作树是不行的。被提交的是索引而不是工作树,这个事实即使不使用钩子也值得了解。

步骤

  1. 创建 /root/gitx9/repo,并把 core.hooksPath 指定为 .githooks。
  2. 用 .githooks/commit-msg 强制标题格式,并把被拒绝的记录保存到 notes/reject.txt。
  3. 用 .githooks/pre-commit 阻止超过 100000 字节的文件,并保存到 notes/bigfile.txt。
  4. 把该钩子改为查看索引,阻止以 AKIA 开头的字符串,并把只看工作树为什么会被绕过的原因保存到 notes/index.txt。
  5. 用 --no-verify 创建一个违反规则的提交,并保存到 notes/bypass.txt。
  6. clone 出 /root/gitx9/clone,把哪些内容会随之带过来、哪些不会,保存到 notes/share.txt。
  7. 创建 /root/gitx9/origin.git,用 .githooks/pre-push 阻止直接向 main push,并保存到 notes/prepush.txt。
  8. 在 notes/report.md 中总结客户端钩子和服务器钩子各自的作用。

参考

决定把钩子放进仓库里

创建 /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 中总结客户端钩子和服务器钩子各自的作用。

一张表加几行规则就够了。务必写下客户端钩子挡不住的三种情况,并以此为由,说明为什么要把同一条规则挂在两个地方。另外,“钩子太慢的话,大家就会养成绕过的习惯”这条实际工作中的建议也很有价值。