繁体   English   中英

Git - 远程:致命:你在一个尚未出生的分支上

[英]Git - remote: fatal: You are on a branch yet to be born

我试图设置一个钩子从我的桌面推送到我的服务器。 这在过去已经成功过无数次,但现在我在设置新站点时遇到错误:

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

根据本指南,我一如既往地完成了与命令相同的系列。

所以在我的服务器上我创建了一个 git 目录。 例如example.git

然后我运行git init --bare 之后我去我的钩子:

cd hooks/
cat > post-receive

在我的 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 会通过互联网电话或等效方式调用另一个 Git。 他们使用一种协议交换信息,该协议可帮助他们识别何时直接相互交谈,以及何时您的 Git 从他们的一端获取的东西不是来自他们的Git ,而是来自他们的 Git 正在使用的东西。

在这种情况下,他们的 Git(在服务器上)正在运行他们的 Git 钩子。 只有一个钩子——你创建了它,所以我们可以称它为“你的”钩子,但是你运行 Git 的计算机不知道服务器上的东西是你创作的:它不知道,不需要知道,并且不在乎; 它只是传递消息。 所以我们称它为“他们的”钩子。

他们的钩子说:

fatal: You are on a branch yet to be born

你会在你的一端看到它以remote:为前缀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

如果您在其他地方单独运行此命令它将检查哪个分支? 这不是一个修辞问题,答案在文档中,尽管它可能不清楚甚至具有误导性:

您可以省略branch ,在这种情况下,命令退化为“检查当前分支”,这是一个美化的无操作,具有相当昂贵的副作用,仅显示当前分支的跟踪信息(如果存在)。

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

当前分支

这就是关键,就在那里:当前分支 这个裸存储库的“当前分支”是什么?

答案与任何 repo(裸或非裸)相同:当前分支是在HEAD文件中命名的分支。 如果你在这个裸存储库中浏览,你会找到那个文件; 检查它,它会说:

$ cat HEAD
ref: refs/heads/master
$ 

换句话说, HEAD将当前分支命名为master ,因为git init这种方式设置它并且此后没有任何更改它。

所以你的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 我看到了两个明显可能的“想要”,尽管你可能想要比这两个更高级的东西:

  1. 您不想导出任何内容,因为没有要导出的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
  2. 您想导出当前 ( master ) 分支以外的内容。 决定这是否意味着“永远”或“直到master出现”或其他什么; 如果需要或需要,修改你的部署脚本,或者只是改变 Git 对当前分支的想法。

假设您希望现在和永远部署master_prefix 通常,您可以使用简单的git checkout将裸master_prefix切换到master_prefix ,但您不能这样做,因为 (1) 它是一个--bare repo 并且 (2) 还没有master_prefix (除非您正在执行此帖子- 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分支。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM