Git:pre-receive hook只允许合并而不是直接提交到master

Mar*_*zer 8 git git-merge githooks git-commit

pre-receive在git远程分支上创建一个钩子有问题,做我想要的.

有什么问题?

不允许直接提交到主分支.只允许合并到主分支中.

到目前为止,我的解决方案是检查主用户受到影响的用户推送是否有变化.但问题是,如果更改是直接提交或合并,我无法区分.

#!/bin/sh
while read oldrefid newrefid refname
do
if [ "$refname" = "refs/heads/master" ]; then
        echo $(git merge-base $oldrefid $newrefid)
        echo "---- Direct commit to master branch is not allowed ----"
        echo "Changes only with a merge from another branch"
        exit 1
fi
done
Run Code Online (Sandbox Code Playgroud)

有谁有想法,如何检查更改是否合并?

谢谢!

tor*_*rek 7

这是简短的回答:看看产生的价值:

git rev-list --count --max-parents=1 $oldrefid..$newrefid
Run Code Online (Sandbox Code Playgroud)

你希望这个为零.继续阅读解释(和警告).


你的循环有正确的轮廓:

  • 阅读所有参考更新;
  • 对于那些更新您关心的分支(或其他参考)的人,请执行一些检查.

诀窍在于执行检查.考虑一下您收到的另外两条信息,即旧的和新的SHA-1 ID,并且在这些钩子中,这两个SHA-1 ID中的一个(但不是两个)可能都是0s(意味着正在创建ref)或删除).

要坚持认为更改不是创建或删除,您的测试应确保SHA-1都不是全零.(如果你愿意假设只需要检查删除,你可以检查新的SHA-1不是全零.但是如果可以进行创建 - 只有在某种情况下master分支被删除后才会出现这种情况例如,某人登录到服务器接收推送,并手动删除它 - 你仍然需要确保旧的SHA-1不是全零的最终测试.显然这种删除是可能的,问题是你是否想编写代码来处理这个案子.)

在任何情况下,最典型的推送只是更新引用.请注意,任何新的提交都已写入存储库(如果拒绝推送,它们将被垃圾收集),因此您的任务是:

  • 查找并验证引用用于命名的任何提交,它将不再命名(这些提交将被非快进推送删除); 和
  • 查找并验证引用现在将命名的任何提交,它不用于命名(这些是通过推送添加的新提交,无论它是否快进:请记住,新推送可以删除一个或者更多提交,同时添加一个或多个).

要找到这两组提交,您应该使用git rev-list,因为这正是它的工作:生成由某个表达式指定的SHA-1列表.您在此处需要的两个表达式是"所有提交ID都可以从一个修订版中找到,但尚未从其他ID中找到".在git rev-list条款这些git rev-list $r1 ^$r2,1或者等价地,git rev-list $r2..$r1对于两个修订说明符$r1$r2.当然,这两个revspecs只是建议的旧ID和新ID push.

这两个ID的顺序决定了哪些提交git rev-list列表:将被删除的那些 - 这个集合对于快进操作是空的 - 以及将要添加的那些.


在这种特殊情况下,您的目标不是自己生成这些提交列表(虽然这样可行),而是这些列表中选择一些内容.

您可能希望阻止提交删除(即,即使用户执行push指定的强制标志,也要强制执行快速转发).在这种情况下,只需验证"要删除"列表是否为空就足够了.您可以通过确保列表实际为空,或者在shell脚本中更简单 - git rev-list为您计算它们并检查结果数字是否为零来实现.

您肯定希望阻止合并的添加,但允许添加.在这种情况下,添加--max-parents=1(也可以拼写--no-merges)会告知git rev-list抑制具有两个或更多父项的提交,即合并.添加--count会使您获得满足此"不是合并因为零或一个父"约束的提交计数.如果此计数为零,则根据定义添加的任何提交必须是合并.

因此:

n=$(git rev-list --count --max-parents=1 $oldrefid..$newrefid)
if [ $n -gt 0 ]; then
    echo "disallowed: push adds $n non-merge commit(s)" 1>&2
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)

例如,足以强制执行此特定约束.


1几乎但不完全等效,你可以写git rev-list $r1 --not $r2:不同之处在于--not徘徊的效果,所以如果你要添加另一个修订版ID,$r3那么--not将适用r3.也就是说,git rev-list A ^B C手段yes-A, not-B, yes-C,但A --not B C手段yes-A, not-B, not-C.请注意,在rev-list语法中,B..A意味着A ^B,即完全B颠倒.


Jan*_*ger 4

您在预接收挂钩中获得的是分支的前一个和新提示,因此您必须检查添加的提交列表,并查看其中是否有任何未合并:

nonmerges=$(git rev-list --no-merges --first-parent $oldrefid..$newrefid | wc -l)
[ "$nonmerges" -eq 0 ] && exit 0
Run Code Online (Sandbox Code Playgroud)

--first-parent将输出限制为来自主线的提交,即跳过合并的提交(可通过第二/第三/...父级访问)。

可能很复杂:快进合并(很难与一系列正常提交区分开来)。