You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于主干的开发:如何仅允许合并提交推送到受保护分支?

限制发布分支仅接受主干来源合并提交的实现方案

针对你团队基于主干的开发模式下的发布分支保护需求,以下是几种可行的实现方式,无需依赖繁琐的额外PR流程:

一、GitHub Actions 提交校验(推荐,GitHub原生支持)

通过Workflow在推送或PR阶段自动校验提交类型和来源,不符合规则的推送会被直接拦截。

实现步骤

  1. 在仓库根目录创建.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 "✅ 所有提交均符合发布分支要求"
  1. 配置GitHub分支保护规则:
    • 开启Require status checks to pass before merging,将上述Protect Release Branch workflow设为必填检查项
    • 关闭Allow force pushes,防止强制覆盖分支
    • 可选:设置Restrict who can push to matching branches,仅允许发布维护者操作

二、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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 00:27:13