如何在Git Pre-Receive Hook中检测推送是否包含Rebase?
判断Git推送中的Rebase操作(Pre-Receive Hook实现)
问题描述
我有一个Git Pre-Receive Hook,希望能够判断某次推送是否包含Rebase操作。我在网上找不到明确的处理方法,想请教各位是否有可行方案?目前我已通过以下代码处理合并请求,但无法处理Rebase:
parent_count=$(git rev-list --parents -n 1 "$rev" | awk '{print NF-1}') if [[ $parent_count -gt 1 ]]; then exit 0 fi
核心思路
Rebase的本质是重写提交历史,会生成全新的提交对象(SHA值改变),但通常会保留原提交的内容。预接收钩子中可以通过「强制推送特征」+「提交内容哈希对比」来识别Rebase操作。
具体实现方案
1. 利用预接收钩子的输入参数
预接收钩子会从标准输入获取每行格式为oldrev newrev refname的内容:
oldrev:推送前分支的HEAD哈希newrev:推送后分支的HEAD哈希refname:推送的目标分支名
2. 初步过滤:判断是否为强制推送
Rebase后几乎必然需要强制推送,可先通过拓扑关系判断:
# 若newrev是oldrev的后代,说明是普通快进/非强制推送,无需检查Rebase if git merge-base --is-ancestor "$oldrev" "$newrev"; then exit 0 fi
3. 深度检查:对比提交内容哈希
遍历新旧提交集合,检查是否存在「内容完全相同但SHA不同」的提交(这是Rebase的典型特征):
# 获取被替换的旧提交集合 old_commits=$(git rev-list "$newrev".."$oldrev") # 获取Rebase生成的新提交集合 new_commits=$(git rev-list "$oldrev".."$newrev") # 遍历新提交,对比内容哈希 for new_commit in $new_commits; do new_content_hash=$(git show "$new_commit" | git hash-object --stdin) for old_commit in $old_commits; do old_content_hash=$(git show "$old_commit" | git hash-object --stdin) if [[ "$new_content_hash" == "$old_content_hash" ]]; then echo "错误:推送包含Rebase操作,禁止提交" exit 1 fi done done
4. 完整钩子示例(结合原有合并判断)
#!/bin/bash while read oldrev newrev refname; do # 处理首次推送(oldrev为全0哈希) if [[ "$oldrev" == "0000000000000000000000000000000000000000" ]]; then exit 0 fi # 原有合并提交判断逻辑 parent_count=$(git rev-list --parents -n 1 "$newrev" | awk '{print NF-1}') if [[ $parent_count -gt 1 ]]; then exit 0 fi # 强制推送+Rebase检查 if ! git merge-base --is-ancestor "$oldrev" "$newrev"; then old_commits=$(git rev-list "$newrev".."$oldrev") new_commits=$(git rev-list "$oldrev".."$newrev") for new_commit in $new_commits; do new_content_hash=$(git show "$new_commit" | git hash-object --stdin) for old_commit in $old_commits; do old_content_hash=$(git show "$old_commit" | git hash-object --stdin) if [[ "$new_content_hash" == "$old_content_hash" ]]; then echo "错误:推送包含Rebase操作,禁止提交" exit 1 fi done done fi done exit 0
注意事项
- 该方案无法覆盖「完全重写提交内容」的极端Rebase场景,但能处理绝大多数常规Rebase操作。
- 若需允许特定分支的Rebase推送,可通过
refname添加分支白名单判断。
内容的提问来源于stack exchange,提问作者Darragh MacKenna
相关产品推荐
相关产品推荐

