我尝试编写服务器端预接收git挂钩来评估提交时的提交。
根据answers here,通过搜索git日志并过滤掉我想要的格式,可以轻松实现这一目标:'。
我已经创建了以下预提交脚本。
#!/bin/bash
set -x #for debugging, TODO: remove
echo "parameters are" $@
echo "1 is " $1
#List of banned users
bannedusers=( root )
author_name=$(git show --pretty=oneline --pretty=format:%an | head -n1)
author_email=$(git show --pretty=oneline --pretty=format:%ae | head -n1)
committer_name=$(git show --pretty=oneline --pretty=format:%cn | head -n1)
committer_email=$(git show --pretty=oneline --pretty=format:%ce | head -n1)
commit_users=( "${author_name}" "${committer_name}" )
for acommituser in "${commit_users[@]}"
do
:
echo $acommituser #for debugging, TODO: remove
for abanneduser in "${bannedusers[@]}"
do
:
echo $abanneduser #for debugging, TODO: remove
if [[ $abanneduser == $acommituser ]]; then
echo "################################################################"
echo "Commits from $abanneduser are not allowed"
echo "git config user.name bob builder --replace-all"
echo "git config user.email bob@aol.com"
echo "git commit --amend --reset-author"
echo "################################################################"
exit 1
fi
done
done
我发现当我在服务器上运行git show
或git log
时,结果是当前 HEAD,而我想查询传入< / em>提交。
如何修改此脚本,以便在尚未收到的&#39;上运行git log
或git show
承诺?
答案 0 :(得分:17)
您需要使用标准输入提供的SHA-1 ID:
while read oldsha newsha refname; do
... testing code goes here ...
done
&#34;测试代码&#34;然后需要查看至少一些以及所有三个项目,具体取决于要执行的测试。
如果建议创建参考名称$oldsha
,0
中的值将为40 $refname
s。也就是说,$refname
(通常类似refs/heads/master
或refs/tags/v1.2
,但refs/
中的任何名称都可以显示:例如refs/notes/commits
现在不存在接收存储库,但将存在,如果允许更改,将指向$newsha
。
如果建议删除引用名称$newsha
,0
中的值将为$refname
秒。也就是说,$refname
现在确实存在并指向对象$oldsha
;如果您允许更改,则该参考名称将被删除。
如果建议更新引用名称$refname
,即它当前指向git对象$oldsha
,并且如果允许更改,则两者的值将为非零值,它将指向改为使用新对象$newsha
。
如果您只是运行git log
或git show
,git会通过运行git rev-parse HEAD
来使用它找到的SHA-1。在典型的接收存储库中,HEAD
是指向refs/heads/master
的符号引用(文件HEAD
字面上包含字符串ref: refs/heads/master
),因此您将看到最顶层的提交在分支master
上(如您所见)。
您需要专门查看正在进入的任何新对象。您如何知道有哪些新对象进入?这取决于提供的$refname
以及可能其他重新命名的内容。
如果要删除refname,则不会有任何新内容。是否将删除任何基础git 对象(垃圾收集)取决于该refname是否为&#34; last&#34 ;对这些对象的引用。例如,假设整个标准输入序列由两个指令组成:
refs/heads/foo
refs/tags/v1.1
进一步假设refs/heads/foo
(分支foo
)指向此提交图表中的F
,并且标记v1.1
指向带注释的标记G
:
A - B - C - D <-- refs/heads/master
\
E - F <-- refs/heads/foo
\
G <-- refs/tags/v1.1
删除分支foo
是&#34;安全&#34;因为带注释的标记G
将通过v1.1
标记保留它们,所以任何提交都不会消失。
删除标记v1.1
是&#34;安全&#34;(ish),因为分支foo
将通过refs/heads/foo
引用保留它们,因此不会撤消提交。 (带注释的标签对象本身会消失。由您决定是否允许这样做)
但是,删除两者 不安全:提交E
和F
将无法访问并将被收集。 (无论如何,这都取决于你。)
另一方面,可能与这两个指令一起,stdin包含第三个指令:
refs/heads/foo2
指向提交H
,提交H
指向提交G
作为其父[编辑:现在重新阅读此内容,我注意G
是提交对象而不是标记对象的明显假设。如果我们假设G
是提交对象,则下面的其余部分是正确的,但上述内容至少有点错误。但是,DAG受外部参考保护的一般想法仍然是正确的,这应该是最有意义的。] 在这种情况下,删除foo
是安全的,因为新分支foo2
将保留提交H
,这将保留提交G
。
做一个完整的分析是棘手的;通常可以做一个允许&#34;安全&#34;的分段分析。操作(无论你决定这些是什么),并强迫用户在一个&#34; safe&#34;中分段推送更新。方式(首先创建分支foo2
,然后仅删除分支foo
作为单独的推送)。
如果您只想查看新提交,那么,对于每个参考更新:
在大多数&#34;正常&#34;预接收钩子你使用下面概述的方法,但我们有替代这个特定的任务。
这是一种简短的修改方法,可以处理最常见,通常最有趣的案例。假设有人建议将refs/heads/foo
从1234567...
更新为9876543...
。该范围内的某些对象可能已经存在,例如,1234567
可能是提交C
的ID,而9876543
是提交E
的ID:
A - B - C <-- refs/heads/foo
\
D - E <-- refs/heads/bar
在这种情况下,这将检查对象D和E.如果刚刚上传D
和E
提交但尚未否引用,也是如此,即建议的更新是添加D
和E
,图表目前如下所示:
A - B - C <-- refs/heads/foo
\
D - E [no reference yet]
在任何一种情况下,都很简单:
git rev-list $oldsha..$newsha
生成您应该查看的对象ID。
对于新的参考文献,没有捷径。例如,假设我们具有上面显示的相同的五个提交,具有相同的refs/heads/foo
但没有refs/heads/bar
,并且实际的提案是&#34;创建refs/heads/bar
指向E
}&#34 ;.在这种情况下,我们应该再次查看提交D
和E
,但没有明显的方法可以了解D
。
只有在某些情况下才有效的非显而易见的方法是找到在给定建议的情况下可以访问的对象,这些对象目前根本无法访问:
git rev-list $newsha --not --all
在这种特殊情况下,这将再次生成D
和E
的ID。
现在让我们考虑您的具体情况,您希望查看所有要提交的提交。这是处理这个问题的一种方法。
对于所有提议的更新:
如果我们有一些删除和,我们已经积累了一些SHA,拒绝尝试:它太难了。让用户将操作分开。
否则,如果我们没有累积的SHA,我们必须删除(或者根本没有 - 不应该发生,但是无害);允许这个(退出0)。
否则我们必须有一些新的SHA-1值。
使用建议的新SHA作为起点,找到所有可以访问的git对象,排除当前可以以任何名称访问的所有对象。这些都是新的对象。
对于每一个提交,检查它是否被禁止。如果是这样,拒绝整个操作(即使某些部分可能成功);和以前一样,它很难弄清楚,所以让用户将好的&#34;好的&#34;来自&#34;坏&#34;的操作的。
如果我们走得这么远,一切都很好;允许整个更新。
以代码形式:
#! /bin/sh
# (untested)
NULL_SHA1="0000000000000000000000000000000000000000" # 40 0's
new_list=
any_deleted=false
while read oldsha newsha refname; do
case $oldsha,$newsha in
*,$NULL_SHA1) # it's a delete
any_deleted=true;;
$NULL_SHA1,*) # it's a create
new_list="$new_list $newsha";;
*,*) # it's an update
new_list="$new_list $newsha";;
esac
done
$any_deleted && [ -n "$new_list" ] && {
echo 'error: you are deleting some refs and creating/updating others'
echo 'please split your push into separate operations'
exit 1
}
[ -z "$new_list" ] && exit 0
# look at all new objects, and verify them
# let's write the verifier function, including a check_banned function...
check_banned() {
if [ "$1" = root ]; then
echo "################################################################"
echo "Commits from $1 are not allowed"
echo ... rest of message ...
exit 1
fi
}
check_commit() {
check_banned "$(git log -1 --pretty=format:%an $1)"
check_banned "$(git log -1 --pretty=format:%cn $1)"
}
git rev-list $new_list --not --all |
while read sha1; do
objtype=$(git cat-file -t $sha1)
case $objtype in
commit) check_commit $sha1;;
esac
done