[对我自己和 OP 的一些提醒:远程origin = GitLab,远程github = GitHub。从根本上说,使用git remote show 的主要问题是它做出了假设。这些假设是关于您将来将如何运行其他 Git 命令(git fetch 和 git push),以及将来如何布置每个 Git 命令中涉及的第二个 Git 存储库。显然,old Danish proverb:很难做出预测,尤其是关于未来。]
让我在这里解决顶部已编辑的问题:
这是让我安心的信息:
mirror pushes to master (up to date)
如何配置 remote/github... 或 push.default... 以获取此输出?
有一件事值得一试:push.default 设置 upstream 告诉 Git 默认情况下,git push 应该这样做。如果这行得通并且做你想要的,那么你就准备好了。但请注意,无论 Git 在这里说什么,这都是谎言。
根本问题是mirror 不推送到master。 在你输入git push 命令之前,它根本不会推送任何东西;一旦你输入了那个命令,你控制where它去with那个git push命令,它可以完全覆盖git remote show所做的任何声明.
您在git push 时间的选择是:
-
不提供额外的参数。
在这种情况下,您的 Git 从当前上游选择 remote。如果上游设置为origin/...,则远程为origin。如果上游设置为github/...,则远程为github。它看起来像一个简单的字符串替换(通常是这样,尽管由于历史原因,它实际上是一个非常复杂的字符串替换,通常结果很简单:在斜线之前取部分)。
此时,您的 Git 继续执行此处列出的第二种情况。
提供一个额外的参数。此参数命名要连接的另一个 Git。它通常是一个远程(origin 或github),但此时您可以提供一个 URL,例如。
提供两个或更多附加参数。其中第一个为远程命名(或者是一个 URL)。其余的是 refspecs,定义如下。
此时,您的 Git 连接到该远程,实际上运行 git ls-remote 以查看它们有哪些分支。此列表很重要,因为它与您提供或未提供的 refspecs 一起使用,具体取决于您的 push.default 设置,并且当您确实提供 refspec 时,您可以只提供 一半一个参考规范。
这是特别成问题的“半个 refspec”案例。不管你做了什么或设置了什么,如果你在mirror上实际运行git push github mirror,你的Git会要求远程github上的Git设置......好吧,这取决于你是否有上游为它设置,如果没有,你的push.default 设置。详细信息在the git push documentation 中,采用 Git 通常的神秘形式。但是,default 默认是“半 refspec”,名称 mirror 表示 mirror:mirror。
如果您提供完整的 refspec,例如 git push github mirror:asdf,则 refspec 的后半部分将确定您的 Git 要求其 Git 设置的分支名称。如果您提供一半 refspec,那么您提供的一半通常会成为两个名称。使用默认的push.default = simple,你不能不小心将你的mirror 推送到任何人 else 的master,你必须使用显式的完整 refspec(然后由你自己决定是否正确)。
如果你给 no refspec,你的 Git 会退回到push.default,并且它只有五个设置。默认的simple 让您的 Git 将您的分支集与其分支集进行比较(来自git ls-remote)。如果你的分支,例如mirror,没有在那里有任何对应的分支,你的 Git 不会要求他们的 Git 设置任何个分支。
假设您在您的分支mirror 上,其上游设置为github/master,并且您已将push.default 配置为upstream(使用git config push.default upstream):
如果您运行不带参数的git push,或不带其他参数的git push github,则远程将为github。
如果您不提供 refspec,则应用 push.default 设置:Git 将构造的 refspec 将是 mirror:master。
但是,如果您提供半个 refspec,我不确定,即使在多次重新阅读文档之后,full refspec Git 会构造什么。我认为会是mirror:mirror,这不是你想要的。
您还可以配置提供默认参考规范的remote.github.push 变量。尽管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 服务器通过礼貌请求将 its master 设置为 Git 通过此推送请求发送的任何哈希 ID。 p>
(如果您在 master 上运行 git push origin 也是如此,同样没有其他参数,除了名称 origin 指的是 GitLab 的 Git。)
另一方面,假设您当前的分支名为mirror:
- 所以你刚刚跑了
git checkout mirror
- 现在你运行
git push github
- 您的 Git 将 - 基于它现在看到的内容,因此假设这就是它再次看到的内容 -不会尝试推送任何东西。
原因很可能是您将push.default 配置或默认为simple。当push.default 设置为simple 时,没有refspec 参数的git push 会尝试推送当前 分支,但只有在另一个Git 有一个同名的分支。所以如果github(即https://github.com/...)的Git 仍然没有有一个名为mirror 的分支,你的Git 会对自己说:嗯。没有名为 mirror 的分支。最好不要推任何东西。
(如果明天 GitHub 上的其他 Git确实有一个名为 mirror 的分支,你的 Git 会告诉自己:啊哈!有一个名为 mirror 的分支!好的,我会要求其他 Git 更新其mirror。)
为什么不“镜像推送到主控”?
因为它没有。即使您重新配置您的 push.default,也没有默认设置意味着“如果我没有给 git push 一个 refspec,则创建一个不匹配的。”
关于 refspecs 的简短部分
所有这些都以 refspec 的概念为基础。在 Git 中,refspec 本质上是一对引用。
reference 是“分支或标签名称”的花哨词。 (它可以不仅仅是这两个,但它们是主要的两个。)像master 这样的分支名称是其完整拼写的简写,refs/heads/master。这个reference 名称refs/heads/master 是Git 表达“不仅仅是master,而是分支 master”的方式。像v1.2 这样的短标签名称是参考名称refs/tags/v1.2 的简写。如果你省略了refs/heads/ 或refs/tags/ 部分,Git 通常会通过查看你现在拥有的分支和标签来判断你的意思。
无论如何,refspec 大多只是其中的 两个,中间有一个冒号 ::
refs/heads/master:refs/heads/master
例如。您需要其中的两个,因为您使用了两个 Git:一个在您的系统上,您要求它做某事,另一个在某些 remote 上,例如 gitlab 或 github。你让你的 Git 通过网络电话呼叫另一个 Git。然后你的 Git 和他们的 Git 相互交谈,之后你的 Git 将 fetch 或 push 东西。
每个 Git 的 fetch 和 push 步骤需要一个引用,这意味着您需要 两个 引用:一个 refspec。
refspec 的两半是 source 和 destination。如果您正在运行git fetch,则源是 other Git,目标是您自己的 Git。如果你正在运行git push,你就是源;另一个 Git 成为目的地。但无论哪种情况,源都会向目标提交一些提交,然后源的 name(refspec 的左半部分)用于更改目标的 name 中的某些内容— refspec 的右半部分。
对于git fetch,两边有不同的名字是完全正常的。我们从他们的refs/heads/master获取并写信我们自己的refs/remotes/origin/master。我们从他们的refs/heads/master 获取并写信给我们自己的refs/remotes/mirror/master。这让我们可以从一堆不同的地方获取数据,同时保持它们都是正确的。
不过,对于 git push,在每一侧使用 same 名称更为正常。我们从他们的master 获取到我们的refs/remotes/.../master。然后我们工作了一段时间,并确保 我们的 master 中的任何内容都是在他们的 master 之上的更新,例如通过合并或变基。然后我们再次打电话给他们,提交我们的新提交,并要求他们将他们的master——不是他们甚至没有的nando/master,而是他们的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 的分支,但是 your Git 为 your refs/heads/master(你的 master 分支)拥有的又大又丑的哈希 ID 是不同。
由于两者不同,你的 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 fetch 和git push 会做什么。 (如果你使用git pull,这意味着运行git fetch,然后运行git merge。git remote show 命令也试图猜测它会做什么。)