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

Git-remote:致命:你在一个尚未出生的分支上,git,github,ssh,Git,Github,Ssh,我正在尝试设置一个钩子,从我的桌面推送到我的服务器。这在过去已经运行了无数次,但现在我在设置新站点时遇到了一个错误: remote: fatal: You are on a branch yet to be born 我像往常一样完成了与commands相同的系列,如 所以在我的服务器上,我制作了一个git目录。例如example.git 然后运行gitinit--bare。然后我去我的钩子: cd hooks/ cat > post-receive 在我的帖子里,我收到了一个地方:

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

remote: fatal: You are on a branch yet to be born
我像往常一样完成了与commands相同的系列,如

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

然后运行
gitinit--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 livessh://username@domain.com:x/home/username/example.git

然后我可以运行:
git push-u live master\u prefix


唯一不同的是我在一个叫做master_的分支中,而不是master。这会导致问题吗?如果会,我需要做什么?

编辑,2020年9月:这变得越来越普遍,因为人们正在将现有的
master
分支重命名为
main
或各种服务器端裸存储库中的类似分支。对于TL;DR,如果您在服务器端裸存储库中执行了此操作,或者使用
git init--bare
创建了一个存储库,而没有更新初始
master
部分,那么您将需要调整此裸存储库中的
HEAD
设置。跳转到如何操作?部分以获取说明


正如您所怀疑的,这是因为您正在推一个名为
master\u prefix
而不是master的分支。至于做什么,那取决于你想发生什么。如果要查看多个选项,请跳到末尾

不过,首先,让我们把它分解一下

任何以以下内容开头的消息:

remote: ...
实际上是来自“另一个人”。当你推(或取)的时候,你的Git会通过互联网电话或类似的方式调用另一个Git。他们使用一个协议来交换信息,该协议可以帮助他们识别何时他们在直接交谈,何时你的Git从他们的终端获得的东西不是来自他们的Git,而是来自他们的Git正在使用的东西

在本例中,他们的Git(在服务器上)正在运行Git的钩子。只有一个钩子是你创建的,所以我们可以称它为“你的”钩子,但是你运行Git的计算机不知道服务器上的东西是你写的:它不知道,不需要知道,也不在乎;它只是传递信息。所以我们称之为“他们的”钩子

他们的钩子上写着:

fatal: You are on a branch yet to be born
你可以看到它在你的一端以
remote:
为前缀,让你知道不是你的Git在说话,而是他们那边的东西


在这一点上,最好的办法是改变视角,假装“你”现在是服务器。在“您”端,您的Git启动并接收内容(成功地,并放入请求的分支,
master\u prefix
),然后运行一个钩子。该钩子触发另一个独立的git checkout命令:

git --work-tree=/home/username/public_html/example.com --git-dir=/home/username/example.git checkout -f
这相当长,所以让我们暂时忽略选项来缩短它。除了设置工作和git目录之外,它只是
git checkout-f

如果您在其他地方单独运行此命令,它将签出哪个分支?这不是一个反问句,答案是肯定的,尽管它可能不清楚,甚至有误导性:

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

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

当前分支

这就是关键所在:当前分支。这个裸存储库的“当前分支”是什么

答案与任何回购协议(无担保或无担保)的答案相同:当前分支机构是
HEAD
文件中指定的分支机构。如果你在这个光秃秃的仓库里翻找,你会找到那个文件;检查它,它会说:

$ cat HEAD
ref: refs/heads/master
$ 
换句话说,
HEAD
命名当前分支,自
git init
将其设置为该方式以来,没有任何更改它是
master

因此,您的
git checkout-f
命令正在尝试签出分支
master

实际上存在哪些分支?您可以通过进入裸存储库并运行
git branch
,找到答案:

$ git branch
  master_prefix
$ 
我是通过GitVersion2.3.0获得的:注意,没有
*master
输出。Git的其他(未来的,真正的)版本可能会向您显示
*master
,因为这是您所在的分支,即使它还不存在

发生什么事了?答案是,每当您创建一个未连接到任何现有版本的新分支时(对于新创建的存储库中的
master
分支总是如此),Git通过将分支名称写入
HEAD
,而不是将任何修订ID写入该分支的相应文件来处理此问题。Git就是这样记录命名分支尚未创建,但一旦您为该分支提供了第一次提交,命名分支就会创建

(如果您使用
git checkout-b newbranch--orphan
新分支将处于相同的“尚未出生”状态。当然,
master
最常见,因为任何全新的空存储库都是这样开始的。)


那怎么办呢? 正如我之前提到的,这实际上取决于你想要发生什么

您有一个新的(最初是空的)裸存储库,其中没有
master
分支(但是有一个post-receive钩子尝试导出当前分支,该分支仍然是空的)<
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
    $ echo ref: refs/heads/master_prefix > HEAD
    $ git symbolic-ref HEAD refs/heads/master_prefix
    $ git --work-tree=... --git-dir=... checkout -f master_prefix