【问题标题】:How can I get the last received branch in a bare git repo?如何在裸 git repo 中获取最后收到的分支?
【发布时间】:2014-12-10 18:22:35
【问题描述】:

我在暂存服务器上设置了一个裸仓库。我从那里拉出,开发,然后推回远程仓库,我有一个接收后挂钩设置,以使用git --work-tree=/server/document/root --git-dir=/path/to/repo checkout -f 将仓库签出到登台服务器 DocumentRoot。但是,这总是签出 master 分支。理想情况下,我希望能够让钩子签出最后收到的分支或最近更新的分支,因为我不希望与 master 合并,直到我对我的更改感到满意并且他们已经过检查登台服务器。这可能吗?如果可以,怎么办?

【问题讨论】:

    标签: git


    【解决方案1】:

    这里有一个问题,因为“最后收到”或“最后更新”的概念没有很好地定义。

    让我们假设这个 repo 是通过push 更新的(可能是一个安全的假设)。现在假设两个不同的push-ers 大约在同一时间启动git push,一个在上午 10:01,一个在上午 10:02。人员 A,从 10:01 开始,将更新推送到分支 A1A2(两者都包含在一次推送中)。人 B,从 10:02 开始,将更新推送到分支 B(仅——更典型的单分支推送)。

    然而,人 A 的网络速度很慢,他的上传需要几分钟才能真正完成,他的建议更新分支 A1A2 才能完成,所以接收 这发生在上午 10:03。 B 人在一个快速的网络上,他的推送提案在 10:02 开始和结束。

    您的 repo 脚本会检查提案并确定它们是允许的,因此分支 B 在 10:02 更新。因此,您将部署分支B,这很容易。

    然后A人的push终于通过了,分支A1A2在10:03同时更新。你现在部署哪个分支?

    此外,B 的推送命令比 A 的晚启动是否重要?


    一旦你找到了这个问题的答案,实际上部署一个特定的分支就很容易了:只需让你的钩子检查那个特定的分支,而不是让 repo 只使用HEAD 分支(通常是@ 987654334@:一个裸仓库仍然有一个HEAD,它通常只是从未被触及)。 (请注意,git 将尝试通过索引优化结帐过程。根据许多其他事情,您可能需要调整或解决这个问题。另请注意,签出特定命名的分支将更改 HEAD,除非您使用语法避免更改HEAD。)

    好的,那么,怎么做?

    当然是在一个钩子中,但让我们通过钩子来运行

    当客户端进行推送时,服务器上会运行三个钩子:

    • 预收
    • 更新
    • 接收后

    每个都有一些不同的目的,尽管实际上只有两个是必要的。首先,客户端上传所有内容(所有提交以及所需的任何树和 blob 对象)。那么:

    1. 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 钩子拒绝推送,则通知客户端推送被完全拒绝,整个事情在这一点上停止。 (拒绝,只需以非零状态退出;接受,以零状态退出。)

    2. git 调用 update 钩子,每个要更新的 ref-name 调用一次,将 ref-name、旧的 SHA-1 和新的 SHA-1 传递给它。更新挂钩应决定是接受还是拒绝此特定更改。

      如果更新挂钩拒绝更改,则通知客户端一个引用更新被拒绝,但推送会继续尝试剩余的更改。

    3. 现在所有 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),因为您无法检查不再存在的分支。

    那里有很多部署脚本,质量参差不齐。抓住一些,翻阅它们,并牢记上述注意事项。

    【讨论】:

    • 非常好的点。我想我更喜欢在服务器端部署最后收到的分支,无论它们何时被推送(因此同时推送 2 个分支,一个快速网络一个慢速网络,一个慢速网络将是部署的一个当两者都完成时)。如果这是有道理的……但是在实践中,我并不真正担心,因为至少目前只有一个人负责推送到登台服务器。我希望这是有道理的,我有点 git newb。
    • 是的,这只是在执行这些操作时需要考虑的事情。我个人认为最好有两个或多个部署区域,一个用于测试,一个用于生产:例如,推送到 master 将始终部署到生产并推送到名称以 sandbox 或 @987654358 开头的任何分支@ 或任何会进入测试区域的东西。
    • 所以,我认为我想做的事情是可能的......现在我只是缺乏“如何”。我确实计划最终像您建议的那样制定部署策略,在我提出我的理由之前,我需要让它在登台上工作。你能解释一下我如何改变那个钩子来检查最近更新的分支吗?
    • 添加了一个大的编辑描述机制。您仍然需要自己决定策略,并编写更多代码(或从其他地方获取)。
    • 是的,很好的补充 - 很好的答案!绝对帮助我更深入地了解正在发生的事情。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2014-11-02
    • 2018-10-01
    • 1970-01-01
    • 2015-04-15
    • 2021-12-16
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多