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 asfeat:orfix:). 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:
- 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).
- Extract the commit message for each commit.
- Validate the message against your format rules.
- 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
- Save this code as
pre-receivein your Git server's repository'shooksdirectory (replace the existingpre-receive.sampleif needed). - Make it executable:
chmod +x hooks/pre-receive - 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
patterninvalidate_commit_message()to match your team's specific rules (e.g., enforce ticket numbers likePROJ-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
相关产品推荐
相关产品推荐

