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

如何仅允许从testing分支向master分支合并提交?

Enforcing Only Testing-to-Master Merges with a Git Pre-Receive Hook

Great question—you absolutely can set up a server-side hook to block pushes to master that aren't merge commits originating from your testing branch. While you're right that the pre-receive hook doesn't explicitly expose the "source branch" of a merge commit, we can work around this by inspecting the commit's parent history and verifying it ties back to testing.

How It Works

The pre-receive hook gets passed three pieces of data for each ref being pushed: the old SHA, new SHA, and ref name. For pushes targeting master, we'll:

  1. Reject any direct (non-merge) commits to master—only merge commits are allowed.
  2. For merge commits, check if at least one of their parent commits exists in the testing branch's history (confirming the merge came from testing).

Example Pre-Receive Hook Script

Create a pre-receive file in your server repository's .git/hooks directory (remove the .sample suffix if it exists) with this code:

#!/bin/bash

# Configure your protected and allowed branches
PROTECTED_BRANCH="refs/heads/master"
ALLOWED_SOURCE="refs/heads/testing"

# Read each line of input from Git (old_sha new_sha ref_name)
while read old_sha new_sha ref_name; do
    # Skip if we're not dealing with the master branch
    if [ "$ref_name" != "$PROTECTED_BRANCH" ]; then
        continue
    fi

    # Get the latest commit SHA of the testing branch
    testing_head=$(git rev-parse "$ALLOWED_SOURCE")

    # Iterate over all commits being pushed to master
    for commit in $(git rev-list "$old_sha..$new_sha"); do
        # Check if this is a merge commit (has 2+ parent commits)
        parent_count=$(git rev-list --parents -n 1 "$commit" | wc -w)
        if [ "$parent_count" -lt 2 ]; then
            echo "❌ ERROR: Direct commits to master are forbidden. Only merge commits from testing are allowed." >&2
            exit 1
        fi

        # Get all parent commits of the merge
        parents=$(git rev-list --parents -n 1 "$commit" | cut -d' ' -f2-)

        # Verify at least one parent is part of the testing branch history
        valid_merge=0
        for parent in $parents; do
            if git merge-base --is-ancestor "$parent" "$ALLOWED_SOURCE"; then
                valid_merge=1
                break
            fi
        done

        if [ "$valid_merge" -eq 0 ]; then
            echo "❌ ERROR: Merge commit $commit to master does not originate from the testing branch. Only merges from testing are permitted." >&2
            exit 1
        fi
    done
done

# If all checks pass, allow the push
exit 0

Setup Steps

  1. Make the script executable:
    chmod +x .git/hooks/pre-receive
    
  2. Test it by trying to push a direct commit to master or a merge from a branch other than testing—the hook should reject these pushes with clear error messages.

Key Notes

  • No direct commits: This script blocks any non-merge commits to master, ensuring all changes come through testing via merges.
  • Flexible validation: Using git merge-base --is-ancestor checks if the parent commit is anywhere in the testing branch's history, not just the latest HEAD—so even if you merge a slightly older testing state, it will still be allowed.
  • Emergency exceptions: If you ever need to bypass the rule (e.g., critical hotfix), you could add a check for a specific keyword in the commit message (like [EMERGENCY]) to allow those pushes—just be cautious with this to avoid breaking your workflow.

You were right to wonder about the lack of explicit source branch data in the pre-receive hook, but by leveraging Git's commit history tools, we can reliably enforce your desired workflow.

内容的提问来源于stack exchange,提问作者VP.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:07:31