Git - 远程: 致命错误: 你当前位于尚未创建的分支上

32

我正在尝试设置一个挂钩,将我的桌面推送到我的服务器。 这在过去已经无数次成功,但现在在设置新站点时会出现错误:

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

根据这份指南,我像往常一样执行了相同的命令序列。

于是在我的服务器上创建了一个git目录,例如example.git

然后我运行git init --bare。之后我进入我的挂钩(hooks):

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分支。如果这是导致问题的原因,我需要做什么?


可能是重复的 http://stackoverflow.com/questions/25583072/git-hook-creation-you-are-on-a-branch-yet-to-be-born - Cockootec
1个回答

58
Edit, September 2020: 这种情况越来越普遍,因为人们正在将现有的master分支重命名为main或类似的名称,存储在各种服务器端裸仓库中。简要概述一下,如果您已经在服务器端裸仓库中执行了此操作 - 或者通过git init--bare创建了一个仓库而没有更新初始的master部分 - 您需要调整此裸仓库中的HEAD设置。请跳到那么该怎么办呢?部分查看说明。
remote: ...

实际上这些信息是来自于“另一个人”。当你进行push(或fetch)操作时,你的Git会通过互联网电话或等效方式联系另一个Git。他们使用一种协议交换信息,该协议帮助它们识别彼此直接交流和你的Git从对方那里获取的不是来自于对方的Git,而是来自于对方Git正在使用的其他东西。

在这种情况下,他们的Git(在服务器上)正在运行他们Git的钩子。只有一个钩子 - 你创建了它,所以我们可以称之为“你的”钩子,但你的电脑上运行的Git并不知道服务器上的东西是由你编写的:它不知道,也不需要知道,也不在意;它只传递消息。因此,我们就称之为“他们的”钩子。

他们的钩子说:

fatal: You are on a branch yet to be born

当你在本地Git中看到以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

这段话有点长,我们可以先忽略选项。除了设置工作目录和git目录之外,只需使用git checkout -f

如果您在其他地方运行此命令 by itself ,它会检出哪个分支? 这不是一个修辞问题,在文档中有答案,尽管可能不够清晰,甚至可能误导:

您可以省略 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。我看到有两个明显的可能“希望”,尽管您可能希望得到比这两个更高级的东西:

  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,但是您不能因为(1)它是--bare存储库并且(2)尚不存在master_prefix(除非您正在进行后推作为修复步骤)。

即使新分支尚不存在,服务器上有两种简单方法可以更新其自己的当前分支概念:

    $ echo ref: refs/heads/master_prefix > HEAD

通过(c)粗暴地完全绕过Git来达到目的,或者:

    $ git symbolic-ref HEAD refs/heads/master_prefix

使用Git可以做同样的事情。

另外,您可以指定要检出的精确分支,供post-receive脚本检查:

    $ git --work-tree=... --git-dir=... checkout -f master_prefix

请注意,这将导致Git在每次推送时更改当前分支(在裸库中)为master_prefix

由于您的钩子没有查看已更新的分支(如果有),因此无法告知要部署哪些分支,除非使用默认值(在HEAD中)或明确部署特定分支(添加参数)。

还值得注意的是一个微妙的技巧:裸库中的index文件记录了已检出到指定工作树的内容。只要您只有一个部署位置和/或一个部署的分支,那么这将没问题。如果您开始变得花哨(例如,在同一台服务器上将master部署到常规服务器,但将test部署到测试服务),则可能需要修改部署脚本,以清除并重建目标或使用多个索引文件。

大多数情况下,以上内容并不重要,直到您开始变得花哨。最重要的是,您必须决定要部署什么,并可能创建一个master分支。


谢谢您详尽而有用的回答! - R. Wenger
7
简而言之,我只需要执行命令 "checkout -f {我的实际分支}" 即可。朋友,谢谢 :) - Raheel Hasan
谢谢。非常详尽的回答。 - undefinederror
非常感谢详细的解释。谢谢。 - Rakesh
真是一个惊人的答案! - Racso
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接