可以获取但是找不到分支 TL;DR长ish

问题描述

这是偶尔让我困惑的 Git 细微差别。有人可以解释这里发生了什么吗?我正在从一个存储库中获取(忽略来自配置的重定向规则)并推送到另一个存储库(应用了来自配置的重定向规则):

$ HOME=/dev/null git fetch origin refs/heads/8.9.170
 * branch                  8.9.170    -> FETCH_HEAD

$ git push origin refs/heads/8.9.170
error: src refspec refs/heads/8.9.170 does not match any

$ git rev-parse refs/heads/8.9.170
refs/heads/8.9.170
fatal: ambiguous argument 'refs/heads/8.9.170': unkNown revision or path not in the working tree.
Use '--' to separate paths from revisions,like this:
'git <command> [<revision>...] -- [<file>...]'

$ cat .git/config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = https://chromium.googlesource.com/v8/v8.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    fetch = +refs/branch-heads/*:refs/branch-heads/*
[branch "master"]
    remote = origin
    merge = refs/heads/master

同时,获取一个不存在的分支/引用会抛出一个明显的错误

$ HOME=/dev/null git fetch origin refs/heads/obvIoUsly/invalid
fatal: Couldn't find remote ref refs/heads/obvIoUsly/invalid

解决方法

TL;DR

考虑使用:

git push origin FETCH_HEAD:refs/heads/8.9.170

有关原因,请参阅详细答案。

长(ish)

您的 .git/config 的这一部分看起来有点奇怪,但并非不可能奇怪:

[remote "origin"]
    url = https://chromium.googlesource.com/v8/v8.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    fetch = +refs/branch-heads/*:refs/branch-heads/* 

第二个 fetch = 行乍一看似乎不太可能匹配任何内容:Git 本身不将名称空间 refs/branch-heads/ 用于任何内容。如果它根本不匹配,那很好;它是无害的。如果它确实匹配某些内容,它将强制更新您获取的任何 refs/branch-heads/ 名称。您明确没有获取任何内容,但起初,这似乎是一件奇怪的事情。

但事实证明,https://chromium.googlesource.com/v8/v8.git 确实 中有一大堆refs/branch-heads/ 名称(我查看了 git ls-remote 并看到了它们)。他们是干什么的,我不知道。它们还有标准的 refs/heads/ 分支名称。因为它们确实有这些 refs/branch-heads/ 名称,所以您应该格外小心,我注意到您可以在下面填写完整的 fetch

同时,这个输出在这里:

$ HOME=/dev/null git fetch origin refs/heads/8.9.170
 * branch                  8.9.170    -> FETCH_HEAD

建议您拥有一个真正古老的 Git 二进制文件。这加上一些其他项目可能是您所有后续麻烦的根源。自 1.8.4 以来的 Git 版本将打印:

$ HOME=/dev/null git fetch origin refs/heads/8.9.170
 * branch                  8.9.170    -> FETCH_HEAD
   <hash>..<hash>          8.9.170    -> origin/8.9.170

因为现代 Git 会根据 fetch = 设置“机会性地更新”任何获取的分支,并且虽然您有一个非标准设置,但在标准设置之前。所以你肯定有一个严重过时的 Git。你仍然可以用它完成你的工作;你只需要更明确,运行:

HOME=/dev/null git fetch origin +refs/heads/8.9.170:refs/remotes/origin/8.9.170

这一次更新refs/remotes/origin/8.9.170(强行,因为加号),或者更简单:

HOME=/dev/null git fetch origin

根据 fetch = 行获取所有内容并更新所有名称。请注意,这将遵守您对 refs/branch-heads/ 实体的​​额外规则,更新所有远程跟踪名称 (refs/remotes/origin/*) 和这些奇怪的名称(无论它们是什么)。

尽管如此,您只是将新的提交哈希 ID 放入特殊的 .git/FETCH_HEAD 文件中,git fetch 将其写入其中,以便 git pull 可以找出刚刚得到的内容取来的。由于您没有运行 git pull,这对您没有多大用处。但这就是我们看到提到 FETCH_HEAD 的输出的原因。

现在,我们可以转到 git push,您需要对其进行更改。您正在使用(并获得):

$ git push origin refs/heads/8.9.170
error: src refspec refs/heads/8.9.170 does not match any

您没有名为 8.9.170 的分支。即使您拥有现代 Git(而不是 1.8.4 之前的 Git),您仍然没有名为 8.9.170分支。相反,您将拥有一个名为 8.9.170远程跟踪名称。因此,此时您有两个选择:

  1. 创建一个名为 8.9.170 的分支。然后,您的命令将按原样运行。

  2. 从您拥有的名称或哈希 ID 推送。

对于选项 1,如果您有一个创建了远程跟踪名称的现代 Git,这会更好。您可以简单地运行 git switch 8.9.170git checkout 8.9.170,这将创建该分支,然后将其检出。或者,为了避免检查它(这需要一点时间:铬源很大),您可以运行 git branch 8.9.170 origin/8.9.170,它从 8.9.170 创建 origin/8.9.170。如果缺少其中任何一项,您可以从 .git/FETCH_HEAD 中提取提交哈希 ID,或使用名称 FETCH_HEAD 创建该分支。

对于更简单的选项 2,您可以运行以下命令:

git push origin FETCH_HEAD:refs/heads/8.9.170

这是前面的 TL;DR。名称 FETCH_HEAD 指(暂时!)由 git fetch 获得的哈希 ID,因为您的 Git 是古老的,未能创建远程跟踪名称。此临时 FETCH_HEAD 存储会持续到 next git fetch,后者会覆盖它,因此必须非常快地完成此操作。 (这就是为什么这适用于 git pull,它只运行 git fetch,然后 立即 运行第二个 Git 命令来使用 .git/FETCH_HEAD 之前的值 他们可以被替换。)