我试图设置一个钩子(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
分支。