必须将Allowed to push设为No one(即push_access_level=0)才能真正锁死分支,否则即使启用MR审核、CI检查等规则,开发者仍可直接git push;还需配合pre-push钩子、通配符保护及权限数值精确配置构成四层防线。

Allowed to push 设为 No one 才算真正锁死
很多人以为只要开了 Protected Branches 就安全了,其实关键在 Allowed to push 这一项。如果它还设成 Developer 或留空,默认就是允许推送——这等于门开着,只挂了个“请敲门”的牌子。
必须手动选 No one,对应 API 参数是 push_access_level=0。否则哪怕你开了 Code Owner Approval、CI 必过、MR 强制审核,普通开发者依然能 git push origin main 直接上。
- GitLab UI 中该选项位于 Settings → Repository → Protected Branches → 编辑分支规则
- 新创建的仓库不会自动继承保护规则,每个项目都得单独配
- 如果团队用 CI/CD 自动化初始化仓库,建议在脚本里补上
curl调用 API 设置保护,避免窗口期被利用
Pre-push 钩子:本地第一道防线
服务端锁死之后,还得防本地手滑。Git 的 pre-push 钩子能在推送前拦截,比等 GitLab 返回 remote: You are not allowed to push code to protected branches 更早止损。
在项目根目录下创建 .git/hooks/pre-push,内容如下:
#!/bin/bash branch=$(git rev-parse --abbrev-ref HEAD) if [[ "$branch" == "main" || "$branch" == "master" ]]; then echo "❌ 错误:禁止直接推送至 $branch 分支" exit 1 fi
- 这个脚本只检查当前分支名,不依赖远程配置,离线也生效
- 注意 chmod +x .git/hooks/pre-push,否则钩子不执行
- 如果团队用 monorepo 或多主分支(如
main+stable),需扩展判断逻辑
Wildcard 保护 feature/* 类分支防扩散
只保 main 不够。一旦有人在 feature/login 上改出问题又合并进 main,风险就转嫁了。GitLab 支持通配符保护,比如把 feature/*、fix/* 全设为 Allowed to push = No one,就能强制所有人走 MR 流程,哪怕只是临时分支。
- Wildcard 规则优先级低于精确匹配,所以
main和feature/*可共存 - 设置路径:Protected Branches 页面底部 “Wildcard protection” 区域
- 别忘了同步更新
pre-push钩子,否则本地仍可能推错到feature/*
强制推送(force push)为什么禁不了?
GitLab 默认禁用强制推送,但这项控制只对已受保护的分支生效,且前提是 Allowed to push 是 No one。如果某人有 Maintainer 权限,又开了 Allowed to push = Maintainer,那他仍可 git push -f —— 因为 GitLab 认为“你有权推,那就连强制也一起给你权限”。
- 真正禁 force push 的唯一方式:确保
push_access_level=0 -
merge_access_level控制的是 MR 合并权限,和推送无关,别混淆 - 紧急回滚时若需临时放开,操作完务必立刻恢复
push_access_level=0,别依赖“我记着要关”
GitLab 的分支保护不是开关式功能,而是由服务端策略、本地钩子、通配符覆盖、权限数值四层叠加构成的防线。最容易被忽略的是:新分支默认不继承保护,而 pre-push 钩子一旦写错或没加执行权限,就等于整条防线缺了一块砖。


















