为何git-revert不触发pre-commit和commit-msg钩子?
git-revert Doesn’t Trigger the Same Hooks as git-commit Great question—this is one of those subtle Git design choices that often catches folks off guard, especially when you’re building hook-based workflows like the one you’re working on (blocking merge commit reverts, which are indeed tricky!). Let’s break this down:
First, confirm your observation is correct
You’re right: git-revert only triggers the prepare-commit-msg and post-commit hooks, while git-commit runs all four: pre-commit, prepare-commit-msg, commit-msg, and post-commit.
Why Git does this
Git’s hook behavior here boils down to two core design principles:
Revert is treated as an automated, "safe" operation
Git seesgit-revertas a predictable, machine-generated commit—it’s not a user-initiated commit where you’re writing custom code or crafting a unique message. Thepre-commithook is meant to validate user-written changes (like running linters, tests, or code checks), andcommit-msgvalidates user-authored messages. Since revert’s changes are auto-generated to undo a specific commit, and its message is standardized (Revert "Original commit message"), Git skips these hooks to avoid unnecessary friction.Avoid breaking automated workflows
Many teams usepre-commithooks that enforce strict code rules. Ifgit-reverttriggered these hooks, it could fail for reasons unrelated to the revert itself—for example, the revert might reintroduce code that was previously flagged by a linter, but that’s intentional (you’re undoing a change). Git wants revert operations to be reliable and unblocked by user-defined checks that aren’t relevant to its purpose.
How to block merge commit reverts (your use case)
Since git-revert skips the hooks you’d normally use for validation, you’ll need to use a different hook type to enforce this rule:
Option 1: Server-side pre-receive or update hooks (recommended)
If you want to block these reverts before they reach your remote repository, server-side hooks are the way to go. These run on the server whenever someone pushes code, so they enforce the rule for everyone on the team.
Here’s a simplified bash example for a pre-receive hook:
#!/bin/bash # Loop through each push reference while read oldrev newrev refname; do # Check every commit being pushed git rev-list "$oldrev..$newrev" | while read commit; do # Get the commit message commit_msg=$(git log --format=%B -n1 "$commit") # Check if it's a revert commit if [[ "$commit_msg" == "Revert "* ]]; then # Extract the original commit being reverted reverted_commit_subject=$(echo "$commit_msg" | grep -oP 'Revert "\K[^"]+') reverted_commit=$(git rev-parse "$reverted_commit_subject" 2>/dev/null) # If the reverted commit exists, check if it's a merge commit (>=2 parents) if [[ -n "$reverted_commit" ]]; then parent_count=$(git log --format=%P -n1 "$reverted_commit" | wc -w) if [[ "$parent_count" -ge 2 ]]; then echo "ERROR: Reverting merge commits is prohibited. Commit $commit reverts merge commit $reverted_commit." exit 1 fi fi fi done done exit 0
Option 2: Client-side pre-push hook
If you want to catch this locally before anyone pushes, a pre-push hook works similarly. It runs right before git push and can abort the push if it detects a merge revert commit. The logic is nearly identical to the pre-receive example—just adjust it to check the commits you’re about to push.
Avoid modifying git-revert itself
You could technically create a custom alias for git revert that runs pre-commit and commit-msg hooks manually before calling the real git-revert, but this is fragile. It breaks Git’s default behavior, won’t work consistently across team members, and could cause unexpected issues with other tools that rely on standard Git hook behavior.
内容的提问来源于stack exchange,提问作者Gnustavo




