问题描述
想要了解Git的pre-receive/update hooks是否串行执行。他正在研究在中央SCM服务器端实施“pre-commit”验证流程的可能性。在这里,“pre-commit”并不是指git的pre-commit hook,而是指在更改成为集成分支的一部分、对所有在该分支上工作的开发人员可见之前的验证。
对于分布式SCM git来说,commit阶段发生在本地服务器上,这不是他所需要的。所以他不能使用git的pre-commit hook。
根据他的了解,pre-receive hook可能是他需要的:
This hook is invoked by ‘git-receive-pack’ on the remote repository, which happens when a ‘git push’ is done on a local repository. Just before starting to update refs on the remote repository, the pre-receive hook is invoked. Its exit status determines the success or failure of the update.
This hook executes once for the receive operation. [snip]
If the hook exits with non-zero status, none of the refs will be updated. If the hook exits with zero, updating of individual refs can still be prevented by the update hook.
或者相关的update hook:
This hook is invoked by ‘git-receive-pack’ on the remote repository, which happens when a ‘git push’ is done on a local repository. Just before updating the ref on the remote repository, the update hook is invoked. Its exit status determines the success or failure of the ref update.
The hook executes once for each ref to be updated, [snip]
但是验证本身可能需要一些时间。
用户的问题是:这些hook的执行是否是串行的(由git控制)?也就是说,如果在前一个hook执行完成之前进行了后续的push操作,那么:
– 是会失败吗?
– 还是会阻塞,直到前一个hook执行完成?
– 还是会与前一个hook执行并行运行?
解决方案
请注意以下操作注意版本差异及修改前做好备份。
方案1
一般来说,Git允许并发操作,因为其中一个操作最终会失败。某些特定的数据库更新会有文件锁(比如更新索引文件),但是多个接收操作可以同时进行。在这种情况下,首先完成验证的操作将被允许,而第二个操作将因为具有无效的历史(即不是快进推送)而失败。
总的来说,这种类型的操作不适合在Git的服务器端钩子中运行,因为如果出现错误,用户将不得不对分支进行一些操作,以使事情恢复到有效状态。这种类型的检查通常在工作站端运行,但是在这种情况下,你会遇到分发Git钩子的“有趣”问题,因为它们实际上不能直接检查。
另一种常见的解决方案是使用像Jenkins这样的CI服务来运行验证(测试)并更新第二个分支或显示可接受提交的实用标签。
方案2
每个Git钩子在运行特定的Git命令时执行。有关详细信息,请参阅Git钩子的文档。如果有两个推送更新相同的引用,那么无论哪个完成了“update”钩子的更新,都将允许更新引用,而另一个将收到错误。这与哪个客户端首先开始推送无关,而是取决于哪个首先更新了引用。
当发生推送时,会运行一系列的钩子。在引用可以更新之前,会运行以下三个钩子:
– pre-push – 客户端,由git push调用,可用于阻止推送的发生。
– pre-receive – 服务器端,由git-receive-pack在远程仓库上调用。在开始更新远程仓库上的引用之前,会调用pre-receive钩子。它的退出状态决定了更新的成功或失败。如果钩子以非零状态退出,则不会更新任何引用。如果钩子以零状态退出,则仍然可以通过update钩子阻止单个引用的更新。
– update – 服务器端,由git-receive-pack在远程仓库上调用。在更新远程仓库上的引用之前,会调用update钩子。它的退出状态决定了引用更新的成功或失败。该钩子对每个要更新的引用执行一次。
我的建议是:你可能问错了问题。任何Git钩子的使用都应该快速简单。如果你的验证需要超过一两秒钟的时间,那么它就太长了,并且会被放错位置。长时间运行的钩子会让用户感到沮丧,因为他们会认为Git没有响应。我的建议是应用“快速失败,经常失败”的原则。
在你的情况下,如果验证需要长时间运行,最好在Git之外使用工具来执行验证,比如单元测试,可以从CI或本地运行。正确使用拉取请求(即合并短期分支)应该检查验证是否通过,然后允许合并这样的更改。
以上是两种解决方案,你可以根据你的需求选择适合你的方案。
总结
在Git中,pre-receive和update hooks的执行是并行的,但是只有第一个完成验证的操作会被允许更新引用,其他的操作会因为无效的历史而失败。因此,如果你的验证需要较长时间,最好将其放在Git之外的工具中执行,并使用拉取请求来确保验证通过后再合并更改。