编辑,2020 年 9 月:这变得越来越普遍,因为人们在各种服务器端裸存储库中将现有的 master 分支重命名为 main 或类似名称。对于 TL;DR,如果您已在服务器端裸存储库中完成此操作,或者使用 git init --bare 创建了一个存储库而没有更新初始 master 部分,您将需要在此调整 HEAD 设置裸仓库。跳转到那该怎么办?部分获取说明。
正如您所怀疑的那样,因为您正在推送一个名为 master_prefix 而不是 master 的分支。至于做什么,那取决于你想要发生什么。如果您想查看多个选项,请跳到最后。
不过,首先,让我们稍微分解一下。
任何以:
开头的消息
remote: ...
实际上来自“其他人”。当您执行推送(或获取,就此而言)时,您的 Git 通过 Internet 电话或等效设备调用另一个 Git。他们使用一种协议交换信息,该协议可帮助他们识别何时直接相互交谈,以及您的 Git 何时从他们那里获取不是来自他们的 Git 的东西,而是来自他们的 Git 所在的东西使用。
在这种情况下,他们的 Git(在服务器上)正在运行他们的 Git 挂钩。只有一个钩子——你创建的,所以我们可以称它为“你的”钩子,但是你运行 Git 的计算机不知道服务器上的东西是你创作的:它不知道,不需要知道,不在乎;它只是传递消息。所以我们称之为“他们的”钩子。
他们的钩子说:
fatal: You are on a branch yet to be born
你会在你的结尾看到它以 remote: 为前缀,让你知道它不是 你的Git 说的东西,它是他们结尾的东西。
此时,最好的办法是改变观点,假装“你”现在是服务器。在“你的”端,你的 Git 启动并接收东西(成功,并放入请求的分支,master_prefix),然后运行一个钩子。该钩子会触发另一个单独的git checkout 命令:
git --work-tree=/home/username/public_html/example.com --git-dir=/home/username/example.git checkout -f
这很长,所以让我们暂时忽略选项来缩短它。除了设置 work 和 git 目录之外,它只是 git checkout -f。
如果您在其他地方单独运行此命令,它将检查哪个分支?这不是一个修辞问题,答案在the documentation,尽管可能不清楚甚至误导:
您可以省略 branch,在这种情况下,命令会退化为“检查当前分支”,这是一个美化的无操作,具有相当昂贵的副作用,仅显示当前分支的跟踪信息(如果存在)。
由于 --work-dir 和 --git-dir 选项以及(裸)存储库可能会更改的事实,它毕竟不是“美化的无操作”,但它确实使用:
当前分支
这就是关键所在:当前分支。这个裸仓库的“当前分支”是什么?
答案与任何 repo 相同(无论是否裸露):当前分支是 HEAD 文件中命名的分支。如果您在这个裸存储库中四处寻找,您会找到该文件;检查它,它会说:
$ cat HEAD
ref: refs/heads/master
$
换句话说,HEAD 命名当前分支,因为 git init 设置它并且此后没有更改它,所以它是 master。
所以您的git checkout -f 命令正在尝试签出分支master。
实际存在哪些分支?您可以通过进入裸存储库并运行 git branch 来找出答案:
$ git branch
master_prefix
$
我用git version 2.3.0 得到了这个:注意没有* master 输出。 Git 的其他(真正的未来)版本可能会向您显示 * master,因为这是您所在的分支——即使它还不存在!
发生了什么事?答案是,每当您创建一个未连接到任何现有修订的新分支时——对于新创建的存储库中的master 分支总是如此——Git 通过将分支名称写入HEAD 来处理这个问题,但不是将任何修订 ID 写入该分支的适当文件中。这是 Git 记录命名分支尚未创建的想法的方式,但一旦您为该分支提供第一个提交,就会创建。
(如果您使用git checkout -b newbranch --orphan,新分支会处于相同的“尚未诞生”状态。当然,master 最常见,因为任何全新的空存储库都是这样开始的。 )
那该怎么办?
正如我之前提到的,这实际上取决于您希望发生什么。
您有一个新的(最初是空的)裸存储库,它没有 master 分支(但是一个尝试导出当前分支的 post-receive 挂钩,它仍然是 master)。然后你从另一个系统提供一个新分支,但它不是master。我看到了两个明显可能的“想要”,尽管您可能想要比这两个更高级的东西:
-
您不想导出任何内容,因为没有要导出的master:修改您的钩子以检查当前分支是否存在:
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。通常,您可以使用简单的 git checkout 将裸仓库切换到 master_prefix,但您不能因为(1)它是 --bare 仓库并且(2)还没有 master_prefix(除非您重新做这个 post-push 作为修复步骤)。
在服务器上,有两种简单的方法可以更新自己对当前分支的想法,即使新分支尚不存在:
$ echo ref: refs/heads/master_prefix > HEAD
通过 (c) 完全绕过 Git 来达到目的,或者:
$ git symbolic-ref HEAD refs/heads/master_prefix
使用 Git 做同样的事情。
或者,您可以指定接收后脚本应该签出的精确分支:
$ git --work-tree=... --git-dir=... checkout -f master_prefix
请注意,这将导致 Git 在每次推送时将当前分支(在裸存储库中)更改为 master_prefix。
由于您的钩子不会查看已更新的分支(如果有),因此除了使用默认值(在HEAD 中)或明确使用之外,没有办法知道要部署哪个分支部署一个特定的分支(添加一个参数)。
还有一点值得注意:裸存储库中的index 文件将记录已签出到指定工作树的内容。只要您部署了一个部署位置和/或一个分支,就可以了。如果您开始花哨(例如,将master 部署到常规服务器,但将test 部署到同一服务器上的测试服务),您可能需要修改部署脚本,以清理并重建目标或使用多个索引文件。
在您开始看中之前,以上大部分内容都无关紧要。主要的是你必须决定你想要部署什么,并且可能创建一个master 分支。