极狐GitLab

用推送规则和分支保护把质量门禁前移到代码落地之前

极狐GitLab
2026年9月29日
11275
分享:

用推送规则和分支保护把质量门禁前移到代码落地之前

定义:推送规则(Push Rules)是极狐GitLab 提供的一组可通过界面配置的 pre-receive Git 钩子,用于在代码真正进入仓库之前校验提交者身份、提交消息、分支名称与文件内容。它与受保护分支、合并请求审批共同构成仓库侧的准入控制;其中推送规则为专业版与旗舰版功能,JihuLab.com 与私有化部署均可用。

大多数团队的门禁都建在流水线上:代码合进去了,跑一遍 SAST、跑一遍单测,红了再退回来修。这套流程没问题,但它有个天然的滞后——错误的代码已经进了仓库历史。清理历史比拦住一次 push 贵得多,尤其是当里面混进了密钥文件、超大二进制或者一条根本查不到需求来源的提交消息时。

把门禁往前挪一格,挪到 git push 那一刻,是成本更低的选择。本文讲的是极狐GitLab 里三件配合使用的东西:推送规则、受保护分支、合并请求审批,以及它们各自的边界和坑。

为什么门禁建在 push 之前更划算

流水线是事后校验,你付出的是一次完整的构建成本,换回一个"不许合"的结论。而 pre-receive 钩子是事前拦截,push 直接失败,代码一个字节都没进仓库。

两者的差异在三类问题上被放大:

  • 密钥泄漏。一旦密钥进了历史,救火方式是轮换密钥 + 清理历史,前者要通知所有使用方,后者要 force push 并让全员重新拉取。

  • 大文件。一个 200MB 的包被推上去之后,仓库体积永久膨胀,每个 clone 都要为此买单。

  • 追溯断链。提交消息里没有需求编号,半年后回溯这次改动为什么做,只能靠翻聊天记录。

这三类问题的共同点是:事后修复的成本远高于事前拦截。这也是推送规则存在的理由。

推送规则能拦住什么

极狐GitLab 把常见的 pre-receive 校验做成了界面开关,不需要你写服务端钩子。校验维度分为五类。

校验提交者身份

  • 拒绝未验证用户:提交者邮箱必须匹配用户已验证邮箱或私有提交邮箱之一。

  • 拒绝不一致的用户名:作者与提交者邮箱一致时,作者名称必须与账号名匹配。

  • 检查提交作者是否为极狐GitLab 用户:作者与提交者的邮箱都必须匹配到已验证的极狐GitLab 用户。

  • 提交作者邮箱:用一个正则约束邮箱域,例如只允许公司域名。

注意一个例外:由极狐GitLab 自身签名的提交(例如通过 UI、仓库镜像产生的提交)使用实例邮箱作为提交者,前三项规则会跳过提交者邮箱检查,改为检查作者邮箱。

校验提交消息与分支名

这两类都用 RE2 语法的正则,每条正则上限 511 字符。

场景

正则示例

效果

提交必须关联需求

JIRA\-\d+

消息里必须出现类似 JIRA-123 的编号

结尾不允许标点

[[:^punct:]]\b$

消息最后一个字符是标点则拒绝

分支名前缀

^JIRA-

分支必须以 JIRA- 开头

分支名长度与字符集

^[a-z0-9\\-]{4,15}$

只接受 4–15 位小写字母、数字、短横线

两个细节值得留意。第一,正则默认多行模式,可以用 (?-m) 关闭。第二,通过 Web UI 创建的提交消息换行符是 \r\n,写多行匹配时用 (\r\n?|\n) 而不是 \n,否则会误判。

另外,默认分支始终被允许;形如 40 位十六进制(类似提交哈希)的分支名出于安全原因默认禁止。

校验文件内容与标签

  • 阻止推送密钥文件:基于一份预定义的文件名模式清单拦截,例如 .aws/credentials、id_rsa、id_ed25519 等。要注意的是,它只拦新推送的文件,对仓库里已经存在的密钥不生效。

  • 禁止的文件名:自定义正则,例如禁止 *.pem、*.p12。

  • 最大文件大小:单位为 MB,设为 0 表示不限制;Git LFS 跟踪的文件不受此限制。

  • 不允许用户使用 git push 删除 Git 标签:防止误删发布标签。

  • 拒绝未签名提交:要求提交必须签名。

怎么配:三个层级与它们的继承语义

推送规则可以在实例级(管理员)、群组级、项目级配置。这里最容易踩的坑是它的继承语义——它是模板,不是继承。

具体规则是:

  1. 全局推送规则作为新项目的模板。配置之后创建的项目,会复制一份当时的规则。

  2. 项目推送规则是独立副本。项目创建后,全局或群组规则再改,项目不会跟着变。

  3. 从项目里删除推送规则,该项目将完全没有任何推送规则,它不会自动回落到群组或实例规则。

  4. 只有项目会继承推送规则,子群组不会从父群组继承。

第 3 条是真正的陷阱。很多团队发现某个项目规则不对,顺手删掉想"让它重新继承",结果发现项目变成了完全不设防的状态。正确做法是重新把项目的规则配成与全局一致,而不是删除。

配置路径:项目 → 设置 → 代码仓库 → 展开「推送规则」→ 保存。

与受保护分支、审批规则配合

推送规则管的是"什么内容能进来",受保护分支管的是"谁能往哪写",审批规则管的是"进来之前谁点头"。三者串起来才是完整的门禁。

受保护分支的关键设置是允许合并与允许推送和合并两个列表:

设置

控制范围

默认值

允许合并

谁能通过合并请求合并变更

无人可以合并(除非有推送和合并权限)

允许推送和合并

谁能直接推送到该分支

无人可以推送

一个常被忽略的点:当「允许推送和合并」未配置时,它不会限制推送访问。想真正禁止直接 push,必须显式把它设为「无人」。

一个典型的最小权限组合是:

  • 允许合并 = 维护者

  • 允许推送和合并 = 无人

这样所有变更都必须走合并请求且由维护者批准。若希望开发者也能合并已批准的 MR,则把「允许合并」设为「开发者 + 维护者」,「允许推送和合并」仍设为「无人」。

分支名支持通配符(区分大小写),例如 production/*、*-stable,多条规则同时命中时取最宽松的那条。

审批规则方面,需要注意的是订阅级别的差异:基础版允许开发者及以上角色审批,但审批是可选的,不会阻止合并;专业版与旗舰版才能配置必需的审批数量与类型、按文件指定代码所有者(CODEOWNERS)、以及实例级与群级别的审批设置。配置路径是项目 → 设置 → 合并请求 → 合并请求审批,也可以通过合并请求审批 API 批量下发。

五个值得提前知道的坑

  1. 正则别写太长。单条上限 511 字符,超了会静默失败。复杂规则建议拆成多条或用「禁止的文件名」这类更简单的开关。

  2. Fork 同步会绕过推送规则。从上游更新 Fork 时,变更直接应用,不走 Fork 的推送规则校验。不要把推送规则当作 Fork 场景下的安全边界。

  3. 删除规则等于解除武装。前面说过,项目级删除后不会回落到上层,这是最容易出事的误操作。

  4. 密钥文件规则不追溯历史。它只拦新推送的文件,历史里已有的密钥要靠 Secret Detection 扫描发现并轮换。

  5. 拒绝未签名提交会误伤。Web IDE 里创建的合法提交可能被拦,需要提前评估团队的实际提交方式。

什么情况下别这么做

  • 团队还在早期、流程未定:先上严格的提交消息正则,只会让大家学会绕过它。先把规范写进文档并跑顺,再固化为机器校验。

  • 分支名规则过于复杂:^[a-z0-9\\-]{4,15}$ 这类强约束会让 fix/temp-try 这种临时分支也推不上去,反而催生本地堆积。

  • 把推送规则当安全合规的全部:它拦不住已经入库的内容,也拦不住 Fork 同步。合规口径上仍需 Secret Detection、SAST 与审计事件配合。

  • 没有维护者带宽的小团队:把「允许推送和合并」设为无人、审批设为必需,会直接把维护者变成瓶颈。

FAQ

Q:推送规则和 CI 里的检查冲突吗?

不冲突,二者作用在不同的时间点。推送规则在 push 时执行,属于服务端 pre-receive 钩子;CI 检查在流水线运行时执行。建议把"格式类、成本极低"的校验放推送规则,"需要构建产物才能判断"的校验放 CI。

Q:能不能只给某些分支配推送规则?

不能。推送规则是仓库级配置,作用于所有 push,不区分分支。分支维度的差异控制要靠受保护分支实现。

Q:已经推上去的密钥文件怎么办?

推送规则无法处理历史数据。应当立即轮换密钥,并用 Secret Detection 做全量历史扫描,必要时清理 Git 历史。


想把这些规则一次性下发到上百个项目,靠界面点是不现实的。可以用合并请求审批 API 与项目级配置 API 做成脚本批量下发,并纳入仓库模板项目的初始化流程,让新项目一出生就带上门禁。

需要一份可直接执行的「仓库准入基线检查清单」?欢迎在评论区留言或联系极狐GitLab 团队获取,我们可以结合你当前的订阅等级与组织架构给出落地方案。