Git+SSH 实现控制分支的提交权限
由于历史的原因,没有引入gitlab,gitea等平台来管理代码,仅使用Git+SSH来实现代码仓库的功能。
现在我们有个需求是能控制分支的提交权限,在网上搜索一顿之后,发现可以依靠Git自带的HOOK机制来实现。当然,也可以引入前述的平台,功能更强大,但考虑到部署成本的原因,决定先解决当下的需求,用自带的就行了。
以下是实现方法:
1、找到git仓库的目录,比如:/home/git/repo/test
2、在test目录下,有个hooks 目录,大概是这样子的:

进入hooks目录,创建名为pre-receive的文件,然后输入以下内容:
#!/bin/bash# ================== 配置区:修改这里 ==================
# 受保护的分支列表(支持精确匹配和正则)
PROTECTED_BRANCHES=("refs/heads/main" # 精确:main"refs/heads/dev" # 精确:dev"refs/heads/release/.*" # 正则:所有 release/* 分支"refs/heads/hotfix/.*" # 正则:所有 hotfix/* 分支
)# 每个分支的允许用户(格式:分支名=用户1,用户2,...)
# 注意:分支名用短名(如 "main"),正则分支用 "release/.*"
declare -A BRANCH_ALLOWED_USERS=(["main"]="admin,alice" # main 只允许 admin 和 alice["dev"]="admin,bob" # dev 只允许 admin 和 bob["release/.*"]="admin" # release/* 只允许 admin["hotfix/.*"]="admin,bob" # hotfix/* 只允许 admin 和 bob
)
# =====================================================# 当前 push 用户(从 SSH authorized_keys 的 environment="GITUSER=xxx" 获取)
PUSH_USER="${GITUSER:-unknown}"# 读取每个 ref 更新(oldrev newrev refname)
while read oldrev newrev refname; do# 只处理分支(忽略 tag)if [[ $refname == refs/heads/* ]]; thenbranch_short="${refname#refs/heads/}" # 提取短分支名,如 "main"# 检查是否是受保护分支is_protected=0for protected in "${PROTECTED_BRANCHES[@]}"; doprotected_short="${protected#refs/heads/}" # 提取短名# 正则匹配(包含 .* 或 * 等)if [[ $protected_short == *.* || $protected_short == *[* || $protected_short == *^ || $protected_short == *\$ ]]; then# 用正则匹配短分支名if [[ $branch_short =~ ^${protected_short}$ ]]; thenis_protected=1breakfielse# 精确匹配if [ "$refname" = "$protected" ]; thenis_protected=1breakfifidone# 如果是受保护分支,检查用户权限if [ $is_protected -eq 1 ]; thenallowed_users="${BRANCH_ALLOWED_USERS[$protected_short]}"if [ -n "$allowed_users" ]; then# 检查用户是否在允许列表IFS=',' read -ra user_list <<< "$allowed_users"is_allowed=0for allowed in "${user_list[@]}"; doif [ "$PUSH_USER" = "$allowed" ]; thenis_allowed=1breakfidoneif [ $is_allowed -eq 0 ]; thenecho "========================================"echo "ERROR: 用户 '$PUSH_USER' 禁止 push 到分支: $branch_short"echo "允许用户: $allowed_users"echo "请联系管理员或使用其他分支。"echo "========================================"exit 1fielse# 如果没定义用户,默认禁止所有echo "========================================"echo "ERROR: 分支 '$branch_short' 已完全锁定,禁止所有用户 push"echo "========================================"exit 1fififi
done# 所有检查通过
exit 03、修改pre-receive文件的权限,增加可执行权限:
chmod +x pre-receive无需重启任何组件和系统,就可以生效了。因此,如果想修改分支或者是用户,可随时进行修改。
以上是增加控制的方法,那么,如何解除控制呢,有2种办法:
1、修改pre-receive文件名,比如改成pre-receive.bk。
2、直接屏蔽代码,比如,不想某个分支被控制,则在那个定义分支数组的PROTECTED_BRANCHES,在指定的分支前加上#,表示注释改行。
3、修改用户也是类似的方法。
如果有多个项目,那就需要在每个项目的hooks目录下,部署pre-receive文件。这么对比起来,操作性和便利性确实不如引入第三方平台。
