在 pre-receive 或 update 钩子中,您会在标准输入上或作为参数获得一个 引用名称、引用的 旧值,以及引用的新值。更新挂钩获得一个引用,作为参数。您可以根据这三个参数对单个参考更新做出单一决定:允许或阻止它。 pre-receive 钩子在其标准输入上获取所有建议的更新,每行一个,并且您必须在做出一个决定之前阅读所有标准输入:允许将所有建议的更新传递到下一步(包括运行更新挂钩,一次一个名称)或拒绝整个更新。
在所有情况下,每个参考都使用其完整的拼写格式。请务必使用所有这些,以免您将 tag X、refs/tags/X 误认为是命名为 branch X 的不同引用 refs/heads/X .同样,不要将分支名称refs/heads/experiment/master 与refs/heads/master 混淆,如果您只是将所有内容从最后一个/ 剥离(即包括),就会发生这种情况。您甚至可能会收到更新既不是分支名称也不是标记名称的引用的请求,例如refs/replace/ 命名空间项或refs/notes/commits。
类似地,旧值和新值是两个完整的 Git 哈希 ID,只是其中一个(绝不是两者)可能是特殊的全零“空哈希”。 null 哈希表示引用正在创建(旧值 null,新值非 null)或销毁(旧值非 null,新值 null)。
查看提交
如果您希望查看所有传入提交的消息,则必须遍历 $oldhash..$newhash 中的每个提交,除非您必须以不同方式处理“正在创建的引用”或“正在销毁的引用”。当一个引用被创建时,有时不可能知道哪个提交,如果有进来了,因为那个新的引用。例如,考虑一下你的 pre-receive 钩子获取的情况:
012...789 aaa...aaa refs/heads/br1
345...eee 321...def refs/heads/br2
000...000 999...999 refs/tags/v2.1
现在,refs/tags/v2.1 正在创建中(它的旧值是全零)。它的新值是999...,大概是一个带注释的标签对象或一个提交对象。但是如果999... 到达一个提交,其父级是888... 并且 888... 之前不在存储库中?那么新创建的标签可能就是提交888...的来源。另一方面,br1 和 br2 两个分支名称也正在更改。如果其中一个也达到888... 怎么办? (例如,这可能是 aaa... 的祖父母提交。)
最接近“新引用引入的提交”的方法是使用git rev-list <hash> --not --all 获取可通过哈希 ID 访问的提交列表,这些提交列表尚未从所有现有引用中访问。这会将新提交 888... 视为可从标签访问,即使它也 将从 br1 可访问(当然,前提是您允许更新到 br1)。
对于您的情况,这可能没问题:如果您因在 br1 上有错误消息而拒绝提交,您也可以因在 v1.2 上有错误消息而拒绝提交,这意味着您拒绝提交两次,但那又怎样?或者它可能不很好,在这种情况下,你必须决定你希望如何处理它。请记住,您的选择是在 pre-receive 挂钩中读取 all 更新,并允许它们全部继续或全部拒绝;或在更新挂钩中检查每次更新一个,并允许该更新继续进行,或拒绝它。在 pre-receive 钩子中,由于您拥有所有更新的全局视图,因此您可以编写花哨的逻辑。在更新钩子中,你的视野很窄,你不能编写花哨的逻辑:你必须保持简单。各有优缺点。
无论如何,一旦你有了更新,你就可以知道哪些提交在更新后可以访问,而哪些提交是不可达的:
git rev-list $newvalue ^$oldvalue | ...
并且您可以从不再可访问的旧参考值中判断哪些提交是可访问的:
git rev-list $oldvalue ^$newvalue | ...
(后者是通过强制推送删除的提交)。在代码的... 部分,您可以读取每个提交哈希并对其进行任何您喜欢的操作,例如:
... | while read hash; do
if git log --format=%B $hash | grep "$expr" >/dev/null; then
# grep found a pattern in the body printed by %B
else
# grep did not find the pattern
fi
done