理解 "git remote show" 命令输出... "Local ref configured for 'git push'" 是什么意思?

5

我有两个远程和两个本地分支:

  • 本地分支“master”正在跟踪远程分支“origin/master”
  • 本地分支“mirror”正在跟踪远程分支“github/master”

这是我的.git/config文件中的内容:

...

[remote "origin"]
    url = http://my.gitlab.com/nandoquintana/repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[remote "github"]
    url = https://github.com/nandoquintana/repo.git
    fetch = +refs/heads/*:refs/remotes/github/*
[branch "master"]
    remote = origin
    merge = refs/heads/master
[branch "mirror"]
    remote = github
    merge = refs/heads/master
[push]
    default = tracking

这是“git remote show origin”的输出结果:
$ git remote show origin 

* remote origin
  Fetch URL: http://my.gitlab.com/nandoquintana/repo.git
  Push  URL: http://my.gitlab.com/nandoquintana/repo.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

$ git remote show github

* remote github
  Fetch URL: https://github.com/nandoquintana/repo.git
  Push  URL: https://github.com/nandoquintana/repo.git
  HEAD branch: master
  Remote branch:
    master tracked
  Local branch configured for 'git pull':
    mirror merges with remote master
  Local ref configured for 'git push':
    master pushes to master (local out of date)

“push”和“pull”命令都可以正常工作:

  • “push”命令将本地分支中提交的编辑内容发送到“她”的远程分支。
  • “pull”命令将远程分支中的提交内容带到“她”的本地分支。

那么,“Local ref configured for 'git push'”为什么是“master pushes to master”而不是“mirror pushes to master”?“local out of date”是什么意思?

在@torek回答后更新:

这里有一些参考:

$ git ls-remote github
455063a9db09977535ac808af5729181b153f4c7    HEAD
455063a9db09977535ac808af5729181b153f4c7    refs/heads/master

$ cat .git/refs/heads/master
ca9e4399058a4998bd7c993f86d6740cfaec820b
$ cat .git/refs/heads/mirror
455063a9db09977535ac808af5729181b153f4c7
$ cat .git/refs/remotes/github/master
455063a9db09977535ac808af5729181b153f4c7

确实,"refs/remotes/github/master"和"refs/heads/master"并不相等。这就是为令"本地过期"的消息出现的原因:

master pushes to master (local out of date)

对我来说,这不是问题,我肯定知道“remotes / github / master”中的代码和本地“master”中的代码是不同的。

然而,“remotes / github / master”和本地“mirror”的代码是相同的。事实上,“refs / remotes / github / master”和“refs / heads / mirror”是相等的。

这条消息将让我感到安心:

mirror pushes to master (up to date)

我该如何配置远程/Github…或push.default…以获得此输出?

当您从本地“镜像”分支推送更改时,您使用的确切命令是什么? - Mark Adelsberger
在镜像分支(“git checkout mirror”)中,只需执行“git push”。 - nandoquintana
也许您可以通过重新应用“mirror”跟踪来检查跟踪是否正常:git branch -u github/master(首先检出镜像)。然后再次运行git remote show github命令。您确定您的推送到了正确的位置吗? - code_fodder
@code_fodder 谢谢你的建议...我很遗憾,重新应用后,输出结果仍然相同... :-( 是的,提交到了正确的位置 :-) - nandoquintana
1个回答

3

[一些提醒,既适用于我也适用于OP:远程origin=GitLab, 远程github=GitHub。使用git remote show的主要问题在于它会做出一些假设。这些假设是关于您将来如何运行其他Git命令-git fetchgit push—以及每个Git命令涉及的第二个Git存储库将来如何布置的。正如古丹麻省理工学院名言所说:预测未来尤其是未来的事情非常困难。]

让我在此处回答上文修改过的问题:

This is the message that would give me some peace of mind:

mirror pushes to master (up to date)

How could I configure remote/github... or push.default... to get this output?

有一件值得尝试的事情:一个push.default设置,upstream,告诉Git默认情况下git push应该这样做。如果这有效并且做到了你想要的,那么你就可以了。但请注意,无论Git在这里说什么,它都是有点不真实的。

根本问题在于mirror不会推送到master。在你输入git push命令之前,它根本不会推送任何东西;一旦你输入了那个命令,你就可以使用git push命令控制它去哪里,这完全可以覆盖git remote show所做出的任何声明。

git push时,您有以下选项:

  • 不提供额外的参数。

    在这种情况下,你的 Git 从当前的上游选择 远程。如果上游设置为 origin/...,则远程是 origin。如果上游设置为 github/...,则远程是 github。它看起来像一个简单的字符串替换(通常确实如此,尽管出于历史原因,它实际上是一个可怕的复杂字符串替换,通常变得简单:取斜杠前面的部分)。

    此时,你的 Git 继续执行此处列出的第二种情况。

  • 提供一个额外的参数。该参数指定要连接的另一个 Git 的名称。通常是一个远程(例如 origingithub),但此时你也可以提供一个 URL。

  • 提供两个或更多额外的参数。其中第一个参数指定远程(或是一个 URL)。其余的是refspecs,如下所定义。

在这一点上,你的Git连接到远程并且实际上运行git ls-remote来查看它们有哪些分支。这个列表很重要,因为它与你给出或未给出的refspecs一起使用,基于你的push.default设置,并且事实上,当你给出一个refspec时,你可以只给出一半的refspec。
“半个refspec”情况尤其棘手。无论你做什么或设置什么,如果你实际运行git push github mirror而你在mirror上,你的Git将会请求远程的github Git去设置...嗯,这取决于你是否为它设置了上游,如果没有,则是你的push.default设置。细节在the git push documentation中,以Git通常的难懂形式呈现。然而,默认的默认值是对于“半个refspec”,名称mirror意味着mirror:mirror
如果您提供了完整的refspec,例如git push github mirror:asdf,则refspec的后半部分确定了Git请求其Git设置的分支名称。如果您提供一半的refspec,则通常给出的一半成为两个名称。使用默认的push.default = simple,您不能意外地将您的mirror推送到任何人master,您必须使用明确的完整refspec(然后由您确定是否正确)。
如果您没有提供refspec,您的Git将回退到push.default,只有五个设置可用。默认的simple会将您的分支集与它们的分支集(来自git ls-remote)进行比较。如果您的分支,例如mirror没有相应的分支,您的Git将不会请求设置任何分支。
假设你在分支mirror上,将其上游设置为github/master,并且已经配置了push.defaultupstream(使用git config push.default upstream):
  • 如果你运行git push没有参数,或者git push github没有额外的参数,那么远程将是github

  • 如果你不提供refspec,则应用push.default设置:Git将构造的refspec将是mirror:master

  • 然而,如果你提供了一半的refspec,即使我重新阅读文档多次后,也不确定Git会构造什么完整的 refspec。我认为它将是mirror:mirror,这不是你想要的。

  • 你还可以配置一个remote.github.push变量,提供默认的refspecs。这也可能允许你获得所需内容,尽管push.default = upstream似乎更简单。

现在我们回到原始答案。:-)


那么,“本地引用配置为'git push'”为什么是“主推送到主”呢?
这意味着:
假设您当前的分支是master(即,您已经运行了git checkout master)
如果您随后运行git push github(而不是git push github more-command-words),Git - 具体来说是您自己的Git - 将尝试将您当前的master推送到https://github.com/nandoquintana/repo.git的master,具体取决于GitHub上的Git服务器如何处理礼貌请求以将其master设置为与此推送请求一起发送的哈希ID。
(如果您在master上并运行git push origin,再次没有其他参数,则相同的情况适用,只是名称origin指的是GitLab上的Git。)
另一方面,假设您当前的分支名为mirror:
  • 因此,您刚刚运行了git checkout mirror
  • 现在运行git push github
  • 基于Git当前所看到的内容,因此假设这是它再次看到的内容,您的Git将不会尝试推送任何内容。

原因很可能是您已经配置或默认配置了push.defaultsimple。当push.default设置为simple时,没有refspec参数的git push尝试推送当前分支,但仅当其他Git具有相同名称的分支时才这样做。因此,如果在github(即在https://github.com/...)的Git仓库中仍然没有名为mirror的分支,则您的Git将对自己说:哎呀。没有名为mirror的分支。最好不要推送任何东西。

(如果明天,GitHub 上的那个 Git 确实有一个名为 mirror 的分支,你的 Git 将对自己说:啊哈!那里就是名为 mirror 的分支!好的,我会请求那个 Git 更新它的 mirror)

为什么不是“mirror 推送到 master”?

因为不是这样的。即使您重新配置了 push.default,也没有默认设置意味着“如果我未能给出 git push 的 refspec,则制定一个不匹配的 refspec。”

关于 refspecs 的简要说明

所有这些都基于一个概念:refspec。在 Git 中,refspec 本质上是一对引用。

一个“引用(reference)”是指“分支或标签名称”的术语(它不仅限于这两个,但这是主要的两个)。像“master”这样的分支名称是其完整拼写“refs/heads/master”的简写。这个“引用(reference)”名称“refs/heads/master”是Git表达“不仅仅是‘master’,而是‘分支(branch) master’”的方式。像“v1.2”这样的短标签名是参考名称“refs/tags/v1.2”的简写。如果你省略了“refs/heads/”或“refs/tags/”部分,Git通常通过查看你现在拥有的分支和标签来确定你的意思。
总之,“refspec”基本上只是两个这样的东西,中间有一个冒号“:”。
refs/heads/master:refs/heads/master

例如。您需要两个引用,因为您正在使用两个Git:一个在您的系统上,您要求它执行某些操作,另一个在某个远程位置,例如gitlab或github。您的Git通过互联网电话呼叫其他Git。然后,您的Git和其Git相互交谈,之后您的Git将获取或推送内容。
提取和推送步骤需要每个Git一个引用,这意味着您需要两个引用:refspec。
refspe​​c的两个部分是源和目标。如果您运行git fetch,则源是其他Git,目标是您自己的Git。如果您运行git push,则您是源;另一个Git成为目标。但无论哪种情况,源都会向目标提供一些提交,然后源的名称(refspec的左半部分)用于更改目标的名称(refspec的右半部分)中的某些内容。
对于git fetch,每一边都有不同的名称是完全正常的。我们从他们的refs/heads/master中获取并写入我们自己的refs/remotes/origin/master。我们从他们的refs/heads/master中获取并写入我们自己的refs/remotes/mirror/master。这样可以让我们从许多不同的地方获取,但保持它们的清晰明了。
然而,对于git push,在每一边使用相同的名称则更加正常。我们从他们的master中获取,进入我们的refs/remotes/.../master。然后我们工作一段时间,并确保我们的master中的任何内容都是一个更新,可以放在他们的master之上,例如通过合并或变基。然后我们再次联系他们,交付我们的新提交,并要求他们将他们的master设置为这个最新的提交,该提交建立在他们以前的提交之上,而不是他们甚至没有的nando/master
我们确保在获取、工作和推送的过程中,使代码建立在他们的基础之上。如果我们和Sofia“竞赛”,即我们同时获取了代码,但她工作得更快,然后在我们之前提交了代码,我们就会遇到“远程拒绝”、“不是快进”错误;我们必须重新获取代码,从GitHub或其他地方获取Sofia的工作成果,并将我们的工作建立在她的基础之上,然后再次尝试推送。
“本地过时”意味着什么?
当您的git remote show通过互联网电话调用远程仓库——具体来说是github,即https://github.com/nandoquintana/repo.git——时,那个“另一个”Git会说:
I have these references:
  refs/heads/master  <some big ugly hash ID>

尝试运行git ls-remote github以查看它们拥有的内容,您将获得完整列表。 您自己的Git有一个名为master的分支,但是您的refs/heads/master - 您的master分支的大而丑陋的哈希ID与您的Git不同。 由于两者不同,您的Git认为您要么“领先” - 您拥有一些他们没有的提交,要么“落后”(他们有一些您没有的提交),或者两者都有。如果您落后了,您的Git无法告诉您落后了多少,但它可以通过查看您的所有提交来确定您“领先”了多少。如果您有提交129bca4f...,其父项为e033fc12...,而他们位于提交e033fc12...,那么您仅领先一个提交。
如果你的Git在你的master分支历史记录中找到了他们的Git的提交哈希ID,那么你就是“领先的”,你可以立即使用git push将新提交发送给他们,要求他们将自己的master设置为129bca4f...,他们可能会接受这些提交并更新他们的master
但是,如果他们有提交930ab988...而你根本没有这个提交,那么你的Git只知道他们有一些你没有的提交。你必须是“落后的”。你可以从他们那里使用git fetch,获取你没有的所有提交,并使用refs/remotes/github/master将它们记住。然后,你可以采取任何必要的措施将这些提交添加到你自己的master中,以使你与他们处于平等状态,既不领先也不落后,并进行任何其他工作,以便你现在“领先于”他们。

由你决定这是否是个好主意,如果是的话,是否要执行。所有 git remote show 做的只是通过互联网电话使用 git ls-remote 来调用它们,并将它们的引用与您的引用进行比较,猜测基于这些结果会发生什么 git fetchgit push。 (如果您使用 git pull,那就意味着 运行 git fetch,然后运行 git mergegit remote show 命令也试图猜测那会做什么。)


非常感谢您对refspecs等方面的解释。 - nandoquintana
我已经更新了我的问题,并在评论中提到了你的答案。 - nandoquintana
我认为你写的是remotes/mirror,但你想表达的是remotes/github :-( - nandoquintana
这是有可能的 - 很难记住名称。让我再重新过一遍…… - torek
顺便提一下,参见 https://stackoverflow.com/q/45984929/1256452 - 如果你设置了上游并将 push.default 设置为 upstream,然后运行 git push <remote>,即使你覆盖了默认的 remoteupstream 设置似乎也会传播。因此,如果你确实将 push.default 设置为 upstream,请小心。 - torek
谢谢您的更新,也感谢您详细的解释。我看到使用(或不使用)“push params”可能会很棘手......现在我更深入地理解了“push”操作 :-) - nandoquintana

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