我试图设置一个钩子(Hook),将其从桌面推送到服务器。在过去,这种方法已经工作了无数次,但是现在在建立新站点时出现错误:

remote: fatal: You are on a branch yet to be born

按照this guide的要求,我和命令一样完成了相同的系列。

因此,在我的服务器上,我制作了一个git dir。例如 example.git

然后,我运行git init --bare。之后,我要着迷了:
cd hooks/
cat > post-receive

在收货后,我放置:
#!/bin/sh
git --work-tree=/home/username/public_html/example.com --git-dir=/home/username/example.git checkout -f

我按Ctrl + D退出保存。然后运行chmod +x post-receive
然后在本地运行:git remote add live ssh://username@domain.com:x/home/username/example.git
然后我可以运行:git push -u live master_prefix
我唯一不同的是我在一个名为master_something的分支中,而不是master。这会引起问题吗?如果是,我该怎么办?

最佳答案

编辑,2020年9月:这变得越来越普遍,因为人们将现有的master分支重命名为各种服务器端裸存储库中的main或类似名称。对于TL; DR,如果您是在服务器端的裸存储库中完成此操作的,或者是在使用git init --bare创建存储库而又未更新初始master部分的情况下,则需要调整此裸存储库中的HEAD设置。跳至那么该怎么办?有关说明,请参见部分。

正如您所怀疑的,这是因为您要推送一个名为master_prefix的分支而不是master。至于该做什么,这取决于您要发生的事情。如果要查看多个选项,请跳到最后。
不过,首先让我们对其进行分解。
任何以以下内容开头的消息:

remote: ...
实际上是来自“其他人”。当您进行推送(或获取操作)时,您的Git会通过Internet电话或类似设备调用另一个Git。他们使用一种协议(protocol)交换信息,该协议(protocol)可以帮助他们识别何时彼此直接交谈,以及您的Git从终端获取东西的时间不是从Git而是从Git正在使用的东西。
在这种情况下,他们的Git(在服务器上)正在运行其Git的挂钩。只有一个钩子(Hook)-您创建了它,因此我们可以将其称为“您的”钩子(Hook),但是运行Git的计算机不知道服务器上的内容是您编写的:它不知道,不需要知道,不在乎;它只是传递消息。因此,我们将其称为“他们的”钩子(Hook)。
他们的钩说:
fatal: You are on a branch yet to be born
然后您会在头上看到它的开头加上remote:,以告知您说的不是您的Git,而是他们的东西。

此时,最好的办法是通过假装“您”现在是服务器来更改观点。在“您的”一端,您的Git启动并接收东西(成功,然后放入所请求的分支master_prefix),然后运行一个钩子(Hook)。该钩子(Hook)触发另一个单独的git checkout命令:
git --work-tree=/home/username/public_html/example.com --git-dir=/home/username/example.git checkout -f
这很长,所以让我们暂时忽略一些选项来缩短它。除了设置work和git目录,它只是git checkout -f
如果您在其他地方单独运行了此命令,它将 check out 哪个分支?这不是一个花言巧语的问题,答案是the documentation,尽管可能不清楚甚至会引起误解:

由于--work-dir--git-dir选项以及(裸露的)存储库可能会更改的事实,它毕竟不是“荣耀的无操作”,但它确实使用:

那就是关键,就在这里:当前分支。这个裸仓库的“当前分支”是什么?
答案与任何回购(无论是否裸露)相同:当前分支是HEAD文件中命名的分支。如果您在这个裸仓库中四处寻找,您将找到该文件。检查一下,它会说:
$ cat HEAD
ref: refs/heads/master
$
换句话说,HEAD为当前分支命名,因为git init以这种方式设置了它,此后没有任何更改,所以它就是master
因此,您的git checkout -f命令正在尝试 check out 分支master
实际存在哪些分支?您可以通过进入裸仓库并运行git branch来找出答案:
$ git branch
  master_prefix
$
我使用git version 2.3.0来实现这一点:请注意,没有* master输出。 Git的其他(实际上是 future 的)版本可能会向您显示* master,因为它是您所在的分支,即使它尚不存在!
这是怎么回事?答案是,只要您创建未连接到任何现有修订的新分支(对于新创建的存储库中的master分支总是如此),Git会通过将分支名称写入HEAD来处理此问题,而不写入任何修订ID放入该分支的适当文件。 Git就是这样记录想法的,即尚未创建命名分支,但是一旦您为该分支提供了第一次提交,便会这样。
(如果使用git checkout -b newbranch --orphan,则新分支将处于相同的“尚未诞生”状态。当然,这对于master最常见,因为这是任何全新的空存储库的启动方式。)

那么该怎么办?
如前所述,这实际上取决于您想要发生的事情。
您有一个没有master分支的新的(最初是空的)裸存储库(但有一个试图导出当前分支的后接收挂钩,它仍然是master)。然后,您从另一个系统提供一个新分支,但它不是master。我看到两个明显的可能“想要”,尽管也许您想要的东西比这两个都更奇特:
  • 您不希望导出任何内容,因为没有master可以导出:修改您的钩子(Hook)以检查当前分支是否存在:
    current_branch=$(git symbolic-ref HEAD) || exit 1
    sha1=$(git rev-parse -q --verify $current_branch) || exit 0
    # ok, the current branch exists; deploy it as usual
    git --work-tree=... --git-dir=... checkout -f
    
  • 您要导出当前分支(master)以外的其他内容。确定这意味着“永远”还是“直到master出现”或其他内容;如果需要或期望,请修改您的部署脚本,或者只是更改Git当前分支的想法。

  • 假设您希望现在和永远部署master_prefix。通常,您可以使用简单的master_prefix将裸仓库切换为git checkout,但是您不能这样做,因为(1)这是--bare仓库,并且(2)还没有master_prefix(除非您以修复步骤)。
    在服务器上,有两种简单的方法可以更新其对当前分支的想法,即使新分支尚不存在:
        $ echo ref: refs/heads/master_prefix > HEAD
    
    通过(c)完全绕过Git来达到目的,或者:
        $ git symbolic-ref HEAD refs/heads/master_prefix
    
    使用Git做同样的事情。
    另外,您可以指定接收后脚本应 check out 的精确分支:
        $ git --work-tree=... --git-dir=... checkout -f master_prefix
    
    请注意,这将导致Git在每次推送时将当前分支(在裸存储库中)更改为master_prefix
    由于您的钩子(Hook)没有查看哪个分支已更新(如果有的话),因此除了使用默认值(在HEAD中)还是明确部署特定分支(添加一个参数)。
    还要注意一点点棘手的问题:裸存储库中的index文件将记录已 check out 到指定工作树的内容。只要您具有单个部署位置和/或单个分支部署,就可以了。如果您开始喜欢(例如,将master部署到常规服务器,但是将test部署到同一服务器上的测试服务),则可能需要修改部署脚本,以清理并重建目标或使用多个索引文件。
    在您开始幻想之前,以上大部分都不重要。最主要的是您必须决定要部署的内容,并可能创建master分支。

    10-06 01:06