Git: pre-receive hook 只允许合并而不是直接提交到 master
Git: pre-receive hook to allow only merges and not direct commits into master
我在 git 远程分支上创建 pre-receive
挂钩时遇到了问题,我想做什么。
有什么问题?
不允许直接提交到 master 分支。只允许合并到 master 分支。
解决方案
到目前为止,我的解决方案是检查来自用户的推送是否有变化,而主服务器是否受到影响。但问题是我无法区分更改是直接提交还是合并。
#!/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
有谁知道如何检查更改是否为合并?
谢谢!
你在预接收挂钩中得到的是分支的前一个和新的提示,所以你将不得不检查已添加的提交列表,看看是否有任何不是合并:
nonmerges=$(git rev-list --no-merges --first-parent $oldrefid..$newrefid | wc -l)
[ "$nonmerges" -eq 0 ] && exit 0
--first-parent
将输出限制为来自主线的提交,即跳过合并的提交(可通过 second/third/... parent 访问)。
可能的并发症:快进合并(很难与一系列正常提交区分开来)。
这里是简短的回答:看看由以下因素产生的价值:
git rev-list --count --max-parents=1 $oldrefid..$newrefid
您希望它为零。继续阅读解释(和注意事项)。
你的循环有正确的轮廓:
- 阅读所有参考更新;
- 对于更新您关心的分支(或其他引用)的那些,执行一些检查。
诀窍在于执行检查。考虑您收到的另外两条信息,即旧的和新的 SHA-1 ID,并且在这些挂钩中,这两个 SHA-1 ID 中的一个(但不是两个)可能都是 0
(意思是正在创建或删除 ref。
要坚持更改不是创建或删除,您的测试应确保 SHA-1 均不为全零。 (如果你愿意假设只需要检查删除,你可以只检查新的 SHA-1 是否不是全零。但是如果创建可能发生——只有在 master
分支毕竟被删除了,例如,有人登录到服务器接收推送,然后手动删除它——你仍然需要确保旧的 SHA-1 在最终测试中不全为零。显然是这种删除是可能,问题是你要不要写代码来处理这种情况。)
无论如何,最典型的推送只是更新引用。请注意,任何新的提交都已写入存储库(如果您拒绝推送,它们将被垃圾收集),因此此时您的任务是:
- 查找并验证引用用于命名的任何提交,它将不再命名(这些提交将被非快进推送删除);和
- 找到并验证引用现在将命名的任何提交,它以前没有命名(这些是将通过推送添加的新提交,无论是否快进:记住新推送可以删除一个或多个提交,同时添加一个或多个提交)。
要找到这两组提交,您应该使用 git rev-list
,因为这正是它的工作:生成由某个表达式指定的 SHA-1 列表。您需要的两个表达式是 "all commit IDs find-able from one revision, that are not already find-able from some other ID"。在 git rev-list
术语中,这些是 git rev-list $r1 ^$r2
、1 或等效的 git rev-list $r2..$r1
,用于两个修订说明符 $r1
和 $r2
.当然,这两个 revspecs 只是提议 push
.
的旧 ID 和新 ID
这两个 ID 的顺序决定了哪一组提交 git rev-list
列出了:将被删除的那些——对于快进操作,这组是空的——以及将被添加的那些。
在这个 特殊的 案例中,您的目标不是自己生成这些提交列表(尽管这会起作用),而是 select 一些东西 来自 这些列表。
您可能希望防止提交删除(即,即使执行 push
的用户指定了强制标志,也能强制快进)。在这种情况下,只需验证 "to be removed" 列表是否为空就足够了。你可以通过确保列表实际上是空的,或者——在 shell 脚本中更简单——让 git rev-list
为你计算它们并检查结果数字是否为零来做到这一点。
您绝对希望阻止 不是 合并的添加,但允许合并的添加。在这种情况下,添加 --max-parents=1
(也可以拼写为 --no-merges
)告诉 git rev-list
抑制具有两个或多个父项的提交,即合并。添加 --count
可以获得满足此 "not a merge because zero or one parent" 约束的提交计数。如果此计数为零,则根据定义,添加的任何提交都必须是合并。
因此:
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
例如,足以执行此特定约束。
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
被倒置。
我在 git 远程分支上创建 pre-receive
挂钩时遇到了问题,我想做什么。
有什么问题?
不允许直接提交到 master 分支。只允许合并到 master 分支。
解决方案
到目前为止,我的解决方案是检查来自用户的推送是否有变化,而主服务器是否受到影响。但问题是我无法区分更改是直接提交还是合并。
#!/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
有谁知道如何检查更改是否为合并?
谢谢!
你在预接收挂钩中得到的是分支的前一个和新的提示,所以你将不得不检查已添加的提交列表,看看是否有任何不是合并:
nonmerges=$(git rev-list --no-merges --first-parent $oldrefid..$newrefid | wc -l)
[ "$nonmerges" -eq 0 ] && exit 0
--first-parent
将输出限制为来自主线的提交,即跳过合并的提交(可通过 second/third/... parent 访问)。
可能的并发症:快进合并(很难与一系列正常提交区分开来)。
这里是简短的回答:看看由以下因素产生的价值:
git rev-list --count --max-parents=1 $oldrefid..$newrefid
您希望它为零。继续阅读解释(和注意事项)。
你的循环有正确的轮廓:
- 阅读所有参考更新;
- 对于更新您关心的分支(或其他引用)的那些,执行一些检查。
诀窍在于执行检查。考虑您收到的另外两条信息,即旧的和新的 SHA-1 ID,并且在这些挂钩中,这两个 SHA-1 ID 中的一个(但不是两个)可能都是 0
(意思是正在创建或删除 ref。
要坚持更改不是创建或删除,您的测试应确保 SHA-1 均不为全零。 (如果你愿意假设只需要检查删除,你可以只检查新的 SHA-1 是否不是全零。但是如果创建可能发生——只有在 master
分支毕竟被删除了,例如,有人登录到服务器接收推送,然后手动删除它——你仍然需要确保旧的 SHA-1 在最终测试中不全为零。显然是这种删除是可能,问题是你要不要写代码来处理这种情况。)
无论如何,最典型的推送只是更新引用。请注意,任何新的提交都已写入存储库(如果您拒绝推送,它们将被垃圾收集),因此此时您的任务是:
- 查找并验证引用用于命名的任何提交,它将不再命名(这些提交将被非快进推送删除);和
- 找到并验证引用现在将命名的任何提交,它以前没有命名(这些是将通过推送添加的新提交,无论是否快进:记住新推送可以删除一个或多个提交,同时添加一个或多个提交)。
要找到这两组提交,您应该使用 git rev-list
,因为这正是它的工作:生成由某个表达式指定的 SHA-1 列表。您需要的两个表达式是 "all commit IDs find-able from one revision, that are not already find-able from some other ID"。在 git rev-list
术语中,这些是 git rev-list $r1 ^$r2
、1 或等效的 git rev-list $r2..$r1
,用于两个修订说明符 $r1
和 $r2
.当然,这两个 revspecs 只是提议 push
.
这两个 ID 的顺序决定了哪一组提交 git rev-list
列出了:将被删除的那些——对于快进操作,这组是空的——以及将被添加的那些。
在这个 特殊的 案例中,您的目标不是自己生成这些提交列表(尽管这会起作用),而是 select 一些东西 来自 这些列表。
您可能希望防止提交删除(即,即使执行 push
的用户指定了强制标志,也能强制快进)。在这种情况下,只需验证 "to be removed" 列表是否为空就足够了。你可以通过确保列表实际上是空的,或者——在 shell 脚本中更简单——让 git rev-list
为你计算它们并检查结果数字是否为零来做到这一点。
您绝对希望阻止 不是 合并的添加,但允许合并的添加。在这种情况下,添加 --max-parents=1
(也可以拼写为 --no-merges
)告诉 git rev-list
抑制具有两个或多个父项的提交,即合并。添加 --count
可以获得满足此 "not a merge because zero or one parent" 约束的提交计数。如果此计数为零,则根据定义,添加的任何提交都必须是合并。
因此:
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
例如,足以执行此特定约束。
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
被倒置。