【发布时间】:2012-04-30 14:08:18
【问题描述】:
我想设置一个 pre-receive hook 来执行这样的逻辑
- 检查是否有任何合并提交
- 检查是否推送了新分支
- 检查是否有多次提交。
如果存在上述任何一项,则与用户交互以检查他是否要推送更改,如果是,则推送更改,否则放弃。
有没有可能在 git 的 pre-receive hook 中实现上述功能?
【问题讨论】:
标签: git
我想设置一个 pre-receive hook 来执行这样的逻辑
如果存在上述任何一项,则与用户交互以检查他是否要推送更改,如果是,则推送更改,否则放弃。
有没有可能在 git 的 pre-receive hook 中实现上述功能?
【问题讨论】:
标签: git
您不能在 pre-receive 钩子中与用户交互,至少一般情况下是这样,因为用户可能在其他机器上执行“ssh”,而标准输入不会发送给用户。 (您可能可以通过检查 uid 和/或用户名等来拼凑一些东西,然后转义到“回调”用户的脚本。我为您留下了那个方向的实验。)您可以进行所有这些检查,不过。
预接收钩子(在标准输入上)获取一系列行:
<old-value> SP <new-value> SP <ref-name> LF
(引自githooks(5))。如果 ref-name 具有以下形式:
refs/heads/<branchname>
然后正在创建、删除或更新给定的分支。如果<old-value> 为 40 0s,则正在创建分支;如果 <new-value> 为 40 0s,则将其删除;否则正在更新。 (除了 refs/heads/* 之外还有其他内容;完整列表请参见 git-send-email 挂钩。)
如果分支正在更新,<old-value> 是它过去指向的提交 ID,<new-value> 是如果允许更新它将指向的提交 ID(这取决于接收钩子和更新钩子)。
这是一个简单地检测您的第二个和第三个案例(加上删除)的钩子。要确定是否有合并,您必须检查 $between 中的每个 rev 并查看是否有任何合并(即,有多个父级)。要停止提交,请退出非零而不是返回 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
这假设您推送到位于/some/path/to/repo.git 中的--bare 克隆。
要创建一个允许推送的等效存储库,您必须创建一个名称以repo.allow.git 结尾的实际并行目录。这个目录应该包含一个实际的文件,HEAD,从普通的 repo 复制而来;目录中的其他所有内容都可以是指向../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(如果它是指向分支的链接,则仅允许它是符号链接,那就是“老方法”做一个象征性的参考)。
普通的git push 失败,您可以改为git push remotehost:/some/path/to/repo.allow.git 使其通过。当然,您的用户可能会一直养成这样做的习惯,从而消除了这次 hack 的全部目的,但也许他们不会。
这一切都假设您的远程仓库是一个类 Unix 主机,当然(即支持符号链接)。
【讨论】:
exec 技巧在本地运行的钩子中很好,但预接收通常在具有非交互式 ssh 的远程计算机上运行:git push ... remote: hooks/pre-receive: line 30: /dev/tty: Device not configured