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

Python新手求助:Git服务器pre-receive钩子提交信息校验及列表交集匹配

Building a Solid Git Pre-Receive Hook for Commit Message Validation (Python)

Hey there! Let's start by pointing out the gaps in your current code and walk through building a complete, functional pre-receive hook for checking commit message formats. Your initial approach has a good start, but it's missing key pieces that make it work with Git's hook system.

Issues with Your Current Code

  • Hardcoded commit lists: In a real pre-receive hook, you can't use a static commit_list—you need to pull the actual commits being pushed from Git's standard input.
  • Basic string matching: Checking for exact matches with intersection() won't work for most commit message format rules (like requiring a prefix such as feat: or fix:). You'll want regex for flexible pattern matching.
  • Incomplete logic: Your loop cuts off mid-implementation, so it doesn't actually filter or flag invalid commits.

How Pre-Receive Hooks Work

Git runs the pre-receive hook on the server before accepting any pushed commits. It reads lines from standard input in this format:

<old-reference> <new-reference> <commit-hash>

For each line, we need to:

  1. Fetch all commits included in the push (from the old ref to the new ref; if old is all zeros, it's a new branch, so we check all commits from the new ref to the root).
  2. Extract the commit message for each commit.
  3. Validate the message against your format rules.
  4. Reject the push if any invalid messages are found.

Complete Python Implementation

Here's a full, working pre-receive hook that follows common commit message conventions (like Conventional Commits):

#!/usr/bin/env python3
import sys
import subprocess
import re

def get_commit_messages(old_ref, new_ref):
    # Handle new branch (old ref is all zeros)
    if old_ref == "0000000000000000000000000000000000000000":
        rev_list_cmd = ["git", "rev-list", new_ref]
    else:
        rev_list_cmd = ["git", "rev-list", f"{old_ref}..{new_ref}"]
    
    # Get all commit hashes in the push
    result = subprocess.run(rev_list_cmd, capture_output=True, text=True, check=True)
    commit_hashes = result.stdout.strip().split()
    
    # Fetch each commit's message (only the subject line)
    commit_messages = []
    for commit_hash in commit_hashes:
        msg_result = subprocess.run(
            ["git", "log", "-1", "--pretty=%B", commit_hash],
            capture_output=True, text=True, check=True
        )
        subject_line = msg_result.stdout.strip().split("\n")[0]
        commit_messages.append((commit_hash, subject_line))
    
    return commit_messages

def validate_commit_message(subject_line):
    # Example regex: requires a prefix like feat:/fix:/docs: followed by a message
    # Adjust this pattern to match your team's rules!
    pattern = re.compile(r'^(feat|fix|docs|style|refactor|perf|test|chore|revert): .+$')
    return pattern.match(subject_line) is not None

def main():
    invalid_commits = []
    
    # Read each line from stdin (Git passes push info here)
    for line in sys.stdin:
        line = line.strip()
        if not line:
            continue
        old_ref, new_ref, _ = line.split()
        
        # Get all commit messages for this push
        commits = get_commit_messages(old_ref, new_ref)
        
        # Check each message against the rule
        for commit_hash, subject in commits:
            if not validate_commit_message(subject):
                invalid_commits.append((commit_hash, subject))
    
    # Reject push if invalid commits exist
    if invalid_commits:
        print("ERROR: Invalid commit message formats found:")
        for commit_hash, subject in invalid_commits:
            print(f"- Commit {commit_hash[:7]}: '{subject}'")
        print("\nCommit messages must follow the format: <type>: <description>")
        print("Valid types: feat, fix, docs, style, refactor, perf, test, chore, revert")
        sys.exit(1)  # Non-zero exit code tells Git to reject the push
    
    # All checks passed
    print("All commit messages are valid!")
    sys.exit(0)

if __name__ == "__main__":
    main()

Setup Steps

  1. Save this code as pre-receive in your Git server's repository's hooks directory (replace the existing pre-receive.sample if needed).
  2. Make it executable:
    chmod +x hooks/pre-receive
    
  3. Test it: Push a commit with an invalid message (like test commit)—Git should reject the push and show your error message.

Customization Tips

  • Adjust the regex: Modify the pattern in validate_commit_message() to match your team's specific rules (e.g., enforce ticket numbers like PROJ-123: fix login bug).
  • Check full messages: If you need to validate more than just the subject line, remove the split("\n")[0] part to use the entire commit message.
  • Add more checks: You could add length limits (e.g., subject line under 72 characters) or forbid certain keywords.

内容的提问来源于stack exchange,提问作者Adam Weremczuk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:07:16