基于主干的开发:如何仅允许合并提交推送到受保护分支?
限制发布分支仅接受主干来源合并提交的实现方案
针对你团队基于主干的开发模式下的发布分支保护需求,以下是几种可行的实现方式,无需依赖繁琐的额外PR流程:
一、GitHub Actions 提交校验(推荐,GitHub原生支持)
通过Workflow在推送或PR阶段自动校验提交类型和来源,不符合规则的推送会被直接拦截。
实现步骤
- 在仓库根目录创建
.github/workflows/protect-release-branch.yml文件,内容如下:
name: Protect Release Branch on: push: branches: - release/** # 替换为你的发布分支命名规则,比如release-v* pull_request: branches: - release/** jobs: validate-commits: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # 拉取完整提交历史用于校验 - name: Check commit validity run: | # 获取本次推送/PR的所有新提交 if [ "${{ github.event_name }}" = "pull_request" ]; then COMMITS=$(git log --pretty=format:"%H" origin/${{ github.base_ref }}..${{ github.head_ref }}) else COMMITS=$(git log --pretty=format:"%H" origin/${GITHUB_REF_NAME}..${GITHUB_SHA}) fi for COMMIT in $COMMITS; do # 1. 检查是否为合并提交(父提交数≥2) PARENT_COUNT=$(git rev-list --parents -n 1 $COMMIT | wc -w | awk '{print $1-1}') if [ $PARENT_COUNT -lt 2 ]; then echo "❌ 提交 $COMMIT 不是合并提交,禁止推送到发布分支" exit 1 fi # 2. 检查合并提交的父提交是否来自主干分支(这里假设主干是main) PARENTS=$(git rev-list --parents -n 1 $COMMIT | cut -d' ' -f2-) TRUNK_ORIGIN=0 for PARENT in $PARENTS; do if git branch --contains $PARENT | grep -q "main"; then TRUNK_ORIGIN=1 break fi done if [ $TRUNK_ORIGIN -eq 0 ]; then echo "❌ 合并提交 $COMMIT 未来自主干分支,禁止推送到发布分支" exit 1 fi done echo "✅ 所有提交均符合发布分支要求"
- 配置GitHub分支保护规则:
- 开启Require status checks to pass before merging,将上述
Protect Release Branchworkflow设为必填检查项 - 关闭Allow force pushes,防止强制覆盖分支
- 可选:设置Restrict who can push to matching branches,仅允许发布维护者操作
- 开启Require status checks to pass before merging,将上述
二、Git预接收钩子(自建Git服务器适用)
如果使用自建Git服务器(如GitLab自建版、Gitea等),可以在服务器端通过预接收钩子直接拦截不符合规则的推送。
钩子脚本示例
在服务器仓库的hooks/pre-receive文件中添加以下内容(记得赋予执行权限chmod +x pre-receive):
#!/bin/bash TRUNK_BRANCH="main" RELEASE_BRANCH_PREFIX="release/" while read OLD_REF NEW_REF REF_NAME; do # 只校验发布分支 if [[ ! $REF_NAME =~ refs/heads/$RELEASE_BRANCH_PREFIX.* ]]; then continue fi # 获取推送的所有新提交 COMMITS=$(git rev-list $OLD_REF..$NEW_REF) for COMMIT in $COMMITS; do # 检查是否为合并提交 PARENT_COUNT=$(git rev-list --parents -n 1 $COMMIT | wc -w | awk '{print $1-1}') if [ $PARENT_COUNT -lt 2 ]; then echo "ERROR: Commit $COMMIT is not a merge commit - rejected" exit 1 fi # 检查父提交是否来自主干 PARENTS=$(git rev-list --parents -n 1 $COMMIT | cut -d' ' -f2-) FOUND_TRUNK_PARENT=0 for PARENT in $PARENTS; do if git branch --contains $PARENT | grep -q "$TRUNK_BRANCH"; then FOUND_TRUNK_PARENT=1 break fi done if [ $FOUND_TRUNK_PARENT -eq 0 ]; then echo "ERROR: Merge commit $COMMIT does not come from trunk branch - rejected" exit 1 fi done done exit 0
三、cherry-pick场景的适配
如果需要将主干的单个提交cherry-pick到发布分支(这类提交不是合并提交),可以在Actions脚本中添加额外校验:
# 补充:允许来自主干的cherry-pick提交 COMMIT_DIFF=$(git show $COMMIT --stat --no-merges) if git log main --grep="$(git show --pretty=format:"%s" $COMMIT)" --oneline | grep -q "$(git show --pretty=format:"%h" $COMMIT)"; then TRUNK_ORIGIN=1 fi
这段代码会检查提交的标题和内容是否在主干分支存在,以此确认cherry-pick的合法性。
内容的提问来源于stack exchange,提问作者DJDMorrison
相关产品推荐
相关产品推荐

