这里有一个问题,因为“最后收到”或“最后更新”的概念没有很好地定义。
让我们假设这个 repo 是通过push 更新的(可能是一个安全的假设)。现在假设两个不同的push-ers 大约在同一时间启动git push,一个在上午 10:01,一个在上午 10:02。人员 A,从 10:01 开始,将更新推送到分支 A1 和 A2(两者都包含在一次推送中)。人 B,从 10:02 开始,将更新推送到分支 B(仅——更典型的单分支推送)。
然而,人 A 的网络速度很慢,他的上传需要几分钟才能真正完成,他的建议更新分支 A1 和 A2 才能完成,所以接收 这发生在上午 10:03。 B 人在一个快速的网络上,他的推送提案在 10:02 开始和结束。
您的 repo 脚本会检查提案并确定它们是允许的,因此分支 B 在 10:02 更新。因此,您将部署分支B,这很容易。
然后A人的push终于通过了,分支A1和A2在10:03同时更新。你现在部署哪个分支?
此外,B 的推送命令比 A 的晚启动是否重要?
一旦你找到了这个问题的答案,实际上部署一个特定的分支就很容易了:只需让你的钩子检查那个特定的分支,而不是让 repo 只使用HEAD 分支(通常是@ 987654334@:一个裸仓库仍然有一个HEAD,它通常只是从未被触及)。 (请注意,git 将尝试通过索引优化结帐过程。根据许多其他事情,您可能需要调整或解决这个问题。另请注意,签出特定命名的分支将更改 HEAD,除非您使用语法避免更改HEAD。)
好的,那么,怎么做?
当然是在一个钩子中,但让我们通过钩子来运行
当客户端进行推送时,服务器上会运行三个钩子:
每个都有一些不同的目的,尽管实际上只有两个是必要的。首先,客户端上传所有内容(所有提交以及所需的任何树和 blob 对象)。那么:
-
git 调用pre-receive 钩子,在标准输入上输入一系列行。每行有两个 SHA-1 值(当前又名“旧”,建议替换或“新”)和一个引用名称。旧 SHA-1 值和新 SHA-1 值中的任何一个(但不能同时)都可以是全零,表示正在创建(之前不存在)或删除(如果允许推送,则不会存在)引用。 pre-receive 钩子应该读取所有行,检查每个 ref-name 和每个提供的 SHA-1,并决定整个推送是被接受还是被拒绝。
请注意,引用名称始终是完全限定的:像 dev/joe 这样的分支是 refs/heads/dev/joe;像v1.2 这样的标签是refs/tags/v1.2;等等。
如果 pre-receive 钩子拒绝推送,则通知客户端推送被完全拒绝,整个事情在这一点上停止。 (拒绝,只需以非零状态退出;接受,以零状态退出。)
-
git 调用 update 钩子,每个要更新的 ref-name 调用一次,将 ref-name、旧的 SHA-1 和新的 SHA-1 传递给它。更新挂钩应决定是接受还是拒绝此特定更改。
如果更新挂钩拒绝更改,则通知客户端一个引用更新被拒绝,但推送会继续尝试剩余的更改。
-
现在所有 refs 都被单独更新或拒绝,git 运行 post-receive 钩子。这个钩子获得与 pre-receive 钩子相同的输入(在标准输入上)。
因为所有更新都完成了,这个钩子一般是部署新版本的好地方。
注意 post-receive 钩子不能停止任何更新,但是由于 git 中的一个小错误,如果它退出非零,客户端会被告知推送失败(至少对于某些版本的 git),所以它应该退出零以避免惊讶。
那么哪些钩子记录和部署哪个钩子?
这部分由你决定。接收后挂钩通常是正确的位置,但您可以在任何您喜欢的地方执行此操作。
如果您选择在 post-receive 挂钩中进行部署,但在较早的挂钩中做出决定,则需要记录该决定。在哪里录制取决于您。如果您在选择部署什么的同一地点进行部署,则您没有必须记录决定,因为同一段代码正在做这两件事,所以它可以记住。 p>
至于如何实现部署,一个非常简单的方法是删除目标工作树,然后使用来自特定提交的文件覆盖工作树的表单执行git checkout。假设分支refs/heads/B 已更新为提交1234567。那么:
rm -rf /server/document/root &&
mkdir /server/document/root &&
git --work-tree=/server/document/root checkout -f B -- .
可以解决问题,而无需更改裸存储库中的 HEAD。
如果你想让 git alter HEAD,并且想让 git 的索引来跟踪正在发生的事情,那就容易多了:
git --work-tree=/server/document/root checkout -f B
在这种情况下,HEAD 将记录最近签出的分支。这也会影响裸存储库的新克隆,这也是您可能想要或不想要它的原因之一。
您如何决定部署哪个分支?您需要编写一些代码,但请考虑以下 shell 片段:
while read oldsha newsha ref; do
case "$ref" in
refs/heads/*)
branch=${ref#refs/heads/}
reftype=branch;;
*)
reftype=unknown;;
esac
done
另外请记住,您需要检查引用是否被删除($newsha 是 40 0s),因为您无法检查不再存在的分支。
那里有很多部署脚本,质量参差不齐。抓住一些,翻阅它们,并牢记上述注意事项。