前言

最近由于团队人员变更,仓库迁移,需要配置某些仓库的目录权限,但是距离上次调整已经过去多年,又是因时间长短而遗忘的知识,故今日进行文章记录(温故而知新)

PS:文章中示例的项目仓库名称 wind-pro ,请根据实际情况更换正确的项目仓库名称

场景说明:

  • 核心开发人员:什么目录都能提交。
  • 业务开发人员:只能提交这 3 个目录:

目录示例:

wind-pro-java/src/main/java/com/windseeker/apis/business/
wind-pro-vue/src/views/business/
wind-pro-react/src/views/business/

业务开发如果提交其他目录,GitLab 服务器直接拒绝 push。


第 1 步:登录 GitLab 服务器

ssh root@GitLab服务器IP

第 2 步:创建核心开发人员名单

mkdir -p /opt/wind-git-hooks

cat > /opt/wind-git-hooks/core-developers.txt <<'EOF'
Lehman
zhangsan
lisi
EOF

把里面的 Lehmanzhangsanlisi 换成核心开发人员的 GitLab 用户名。

注意:这里填的是 GitLab 登录用户名,不是姓名。

然后设置权限:

chown root:git /opt/wind-git-hooks/core-developers.txt
chmod 640 /opt/wind-git-hooks/core-developers.txt

第 3 步:获取项目仓库信息

下面两种方式二选一

1.管理员直接获取

进入 GitLab 管理后台:

Admin Area -> Overview -> Projects -> wind-pro

记录两个值:

- Storage name,通常是: default
- Relative path,类似:@hashed/xx/xx/xxxxxxxxxxxxxx.git

2.命令行获取

执行:

gitlab-rails runner "p=Project.find_by_full_path('product/wind-pro'); puts p.repository_storage; puts p.disk_path"

这里的 product/wind-pro 要换成 GitLab 上真实的项目路径。

比如浏览器地址是:

https://gitlab.xxx.com/product/wind-pro

那就是:

product/wind-pro

命令执行后会输出两行,类似:

default
@hashed/xx/xx/xxxxxxxxxxxxxx

最终获取这两个值:

第一行:default
第二行:@hashed/xx/xx/xxxxxxxxxxxxxx.git

第 4 步:创建拦截脚本

执行:

mkdir -p /opt/wind-git-hooks/custom_hooks

cat > /opt/wind-git-hooks/custom_hooks/pre-receive <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

ZERO="0000000000000000000000000000000000000000"
EMPTY_TREE="4b825dc642cb6eb9a060e54bf8d69288fbee4904"
CORE_USERS_FILE="/opt/wind-git-hooks/core-developers.txt"

is_core_user() {
  local user="${GL_USERNAME:-}"
  [ -n "$user" ] && grep -Fxq "$user" "$CORE_USERS_FILE"
}

is_allowed_path() {
  case "$1" in
    wind-pro-java/src/main/java/com/windseeker/apis/business/*) return 0 ;;
    wind-pro-vue/src/views/business/*) return 0 ;;
    wind-pro-react/src/views/business/*) return 0 ;;
    *) return 1 ;;
  esac
}

changed_paths() {
  local oldrev="$1"
  local newrev="$2"
  local base

  if [ "$oldrev" = "$ZERO" ]; then
    base="$EMPTY_TREE"
  else
    base="$oldrev"
  fi

  git diff --name-status -M "$base" "$newrev" | while IFS=$'\t' read -r status path1 path2 rest; do
    case "$status" in
      R*|C*)
        echo "$path1"
        echo "$path2"
        ;;
      *)
        echo "$path1"
        ;;
    esac
  done
}

if is_core_user; then
  exit 0
fi

bad_paths="$(mktemp)"
trap 'rm -f "$bad_paths"' EXIT

while read -r oldrev newrev refname; do
  [ "$newrev" = "$ZERO" ] && continue

  if [[ "$refname" == refs/tags/* ]]; then
    echo "GL-HOOK-ERR: 普通业务开发不允许创建或修改 tag,请联系核心开发人员。"
    exit 1
  fi

  while IFS= read -r path; do
    [ -z "$path" ] && continue

    if ! is_allowed_path "$path"; then
      echo "$path" >> "$bad_paths"
    fi
  done < <(changed_paths "$oldrev" "$newrev")
done

if [ -s "$bad_paths" ]; then
  echo "GL-HOOK-ERR: Push 被拒绝。"
  echo "GL-HOOK-ERR: 业务开发只能修改以下 business 目录:"
  echo "GL-HOOK-ERR: - wind-pro-java/src/main/java/com/windseeker/apis/business/**"
  echo "GL-HOOK-ERR: - wind-pro-vue/src/views/business/**"
  echo "GL-HOOK-ERR: - wind-pro-react/src/views/business/**"
  echo "GL-HOOK-ERR:"
  echo "GL-HOOK-ERR: 本次提交包含以下非业务目录文件:"
  sort -u "$bad_paths" | sed 's/^/GL-HOOK-ERR: - /'
  echo "GL-HOOK-ERR:"
  echo "GL-HOOK-ERR: 如确实需要修改这些文件,请联系核心开发人员二次确认。"
  exit 1
fi

exit 0
EOF

设置权限:

chmod 755 /opt/wind-git-hooks/custom_hooks/pre-receive
chown -R root:git /opt/wind-git-hooks

第 5 步:安装到 wind-pro 仓库

先打包:

cd /opt/wind-git-hooks
tar -cf custom_hooks.tar custom_hooks

然后执行安装命令。

假设第 3 步查出来的是:

default
@hashed/xx/xx/xxxxxxxxxxxxxx.git

那么执行:

cat custom_hooks.tar | sudo -u git -- /opt/gitlab/embedded/bin/gitaly hooks set \
  --storage default \
  --repository @hashed/xx/xx/xxxxxxxxxxxxxx.git \
  --config /var/opt/gitlab/gitaly/config.toml

重点:

- `--storage` 后面填第 3 步第一行。
- `--repository` 后面填第 3 步第二行,并且确保末尾为 `.git`。

第 6 步:测试

让业务开发人员提交这个文件,应该成功:

wind-pro-vue/src/views/business/test/index.vue

再让他提交这个文件,应该失败:

wind-pro-vue/package.json

失败时会看到类似:

Push 被拒绝。
业务开发只能修改以下 business 目录。
本次提交包含以下非业务目录文件:
- wind-pro-vue/package.json

然后让核心开发人员提交 package.json,应该成功。

维护说明

新增核心开发人员,只改这个文件:

vim /opt/wind-git-hooks/core-developers.txt

不用重启 GitLab。
如果以后新增业务目录,比如再允许:

wind-pro-vue/src/api/business/

就修改:

vim /opt/wind-git-hooks/custom_hooks/pre-receive

is_allowed_path() 里面加一行,然后重新执行第 5 步安装。


这套方案的效果就是:普通业务开发人员在本地可以随便改,但只要 push 到 GitLab,服务器会自动检查,不符合目录规则就直接拒绝。

Last modification:May 14, 2026
如果觉得我的文章对你有用,您可以给博主买一杯果汁,谢谢!