我想设置一个预接收钩子,它执行这样的逻辑
- 检查是否有任何合并提交
- 检查是否推送了新分支
- 检查是否有多个提交。
如果存在上述任何一项,则与用户交互以检查他是否要推送更改,如果是,则推送更改,否则放弃。
是否有可能在 git 的预接收挂钩中实现上述目标?
我想设置一个预接收钩子,它执行这样的逻辑
如果存在上述任何一项,则与用户交互以检查他是否要推送更改,如果是,则推送更改,否则放弃。
是否有可能在 git 的预接收挂钩中实现上述目标?
您不能在 pre-receive 钩子中与用户交互,至少一般情况下是这样,因为用户可能在其他机器上执行“ssh”,并且标准输入不会发送给用户。(您可以通过检查 uid 和/或用户名等来拼凑一些东西,然后转义到“回调”用户的脚本。我为您留下了那个方向的实验。)您可以进行所有这些检查, 尽管。
pre-receive 钩子(在标准输入上)获取一系列行:
<old-value> SP <new-value> SP <ref-name> LF
(引自githooks(5)
)。如果 ref-name 具有以下形式:
refs/heads/<branchname>
然后正在创建、删除或更新给定的分支。<old-value>
如果是 40秒,则正在创建分支0
;如果<new-value>
是 400
秒,则将其删除;否则正在更新。(除了 refs/heads/* 之外还有其他东西;完整列表请参见 git-send-email 钩子。)
如果分支正在更新,<old-value>
是它过去指向<new-value>
的提交 ID,并且是如果允许更新它将指向的提交 ID(这取决于预接收挂钩和更新挂钩)。
这是一个简单地检测您的第二个和第三个案例(加上删除)的钩子。要确定是否有合并,您必须检查每个 rev in$between
并查看是否有任何合并(即,有多个父级)。要停止提交,请退出非零而不是返回 0。
#! /bin/sh
check()
{
local old=$1 new=$2 longref=$3
local between rev
if expr $old : '^00*$' >/dev/null; then
echo creating new branch ${longref#refs/heads/}
return 0
fi
if expr $new : '^00*$' >/dev/null; then
echo removing branch ${longref#refs/heads/}
return 0
fi
between=$(git rev-list $old..$new)
case "$between" in
*$'\n'*)
echo at least two revs
for rev in $between; do git log -1 --oneline $rev; done
return 0
esac
echo only one rev
return 0
}
while read old new longref; do
case $longref in
refs/heads/*) check $old $new $longref;;
esac
done
(您可以检查所有转速,$between
因为它们已经发送到远程仓库,即使您要拒绝它们)。
我修改(并重命名)了该check
功能。在新名称下get_confirmation
,它旨在找出(并为默认情况下不希望允许推送的情况返回 0(并为允许推送的情况返回 1)的情况。
然后,在主循环中,您可以这样做:
case $longref in
refs/heads/*)
if get_confirmation $old $new $longref; then
case $PWD in
*.allow.git)
echo 'allowed via alternate path'
;;
*.git)
echo "denied ... push to ${PWD%.git}.allow.git to allow"
exit 1
;;
*)
echo "denied, don't know where I am"
exit 1
;;
esac
fi
;;
# add more cases here if desired
esac
这假设您推送到--bare
位于/some/path/to/repo.git
.
要创建允许推送的等效存储库,您必须创建一个名称以repo.allow.git
. 这个目录应该包含一个实际的文件,HEAD
,从普通仓库复制而来;目录中的所有其他内容都可以是指向以下内容的符号链接../repo.git/<same>
:
cd /some/path/to
mkdir repo.allow.git
cd repo.allow.git
ln -s ../repo.git/* .
rm HEAD; cp ../repo.git/HEAD .
HEAD
必须是普通文件的原因是否则git push
不会将其视为有效的 repo(如果它是指向分支的链接,则仅允许它是符号链接,这是执行符号 ref 的“旧方式”) .
常规git push
失败,您可以改为git push remotehost:/some/path/to/repo.allow.git
使其通过。当然,您的用户可能会一直养成这样做的习惯,从而消除了此 hack 的全部目的,但也许他们不会。
当然,这一切都假设您的远程仓库是一个类 Unix 主机(即支持符号链接)。