我有两个远程和两个本地分支:
- 本地分支"master"正在跟踪远程分支"origin/master">
- 本地分支"镜像"正在跟踪远程分支"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)
"推"one_answers"拉"命令都能正常工作:
- "push"命令将本地分支中提交的编辑发送到"her"远程分支
- "pull"命令将提交从远程分支带到"her"本地分支
那么,为什么"为‘git push’配置的本地ref"是"master pushs to master"?为什么不"镜像推主"呢?"本地过时"是什么意思?
在@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"one_answers"refs/heads/master"是不相等的。这就是为什么出现"本地过期"消息的原因:
master pushes to master (local out of date)
这对我来说不是问题,我确实知道"remotes/github/master"和本地"master"中的代码是不同的。
尽管如此,"remotes/github/master"和本地"mirror"中的代码是相同的。事实上,refs"refs/remotes/github/master"one_answers"refs/heads/mirror"是相等的。
这条信息会让我安心:
mirror pushes to master (up to date)
如何配置远程/github。。。或者push.default…来获得这个输出?
[提醒我自己和OP:remoteorigin
=GitLab,remotegithub
=GitHub。从根本上讲,使用git remote show
的主要问题是它做出了的假设。这些假设是关于你将来将如何运行其他Git命令git fetch
和git push
,以及这些Git命令中涉及的第二个Git存储库将来将如何布局r、 显然,丹麦有句古老的谚语:很难做出预测,尤其是对未来的预测]
让我在这里回答顶部经过编辑的问题:
这条消息会让我安心:
mirror pushes to master (up to date)
如何配置远程/github。。。或者push.default…来获得这个输出?
有一件事值得一试:一个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。它通常是远程(
origin
或github
),不过现在你可以给出一个URL。 -
提供两个或多个附加参数。这些名称中的第一个是远程(或是URL)。其余为参考规范,定义如下。
此时,你的Git连接到那个远程,实际上,运行git ls-remote
来查看它们有哪些分支。这个列表很重要,因为它与您根据push.default
设置给出或未给出的refspec一起使用,而且当您给出refspec时,您只能给出一半的refspec。
特别有问题的是"半个refspec"的情况。无论你做什么或设置什么,如果你在mirror
上运行git push github mirror
,你的Git都会要求远程github
上的Git设置。。。好吧,这取决于你是否有一个上行设置,如果没有,你的push.default
设置。详细信息在git push
文档中,以Git通常的神秘形式。不过,默认默认值是,对于"半参考规范",名称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将你的分支集与他们的分支集(来自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
设置:refspec Git将构造为mirror:master
。 -
然而,如果你提供了半个refspec,我不确定,即使在重读了几次文档之后,完整的refspec Git会构建什么。我认为它将是
mirror:mirror
,这不是你想要的。 -
您还可以配置一个
remote.github.push
变量,该变量提供默认的refspec。这也可以让你得到你想要的,尽管push.default = upstream
看起来更简单。
我们现在回到原来的答案。:-)
那么,为什么"为‘git push’配置的本地ref"是"master pushs to master"?
这意味着:
- 假设您当前的分支是
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
设置为Git通过此推送请求发送的任何哈希ID。
(如果您在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的简短片段
所有这些都基于参考规范的概念。在Git中,refspec本质上是一对引用。
引用是"分支或标记名称"的花哨单词。(它可能不仅仅是这两个,但这是主要的两个。)像master
这样的分支名称由于其完整拼写refs/heads/master
而很短。这个引用名称refs/heads/master
是Git用来表示"不仅是master
,还有分支master
"的方式。像CCD_ 84这样的短标签名称是参考名称CCD_。如果你去掉refs/heads/
或refs/tags/
部分,Git通常会通过查看你现在拥有的分支和标签来判断你指的是哪一个。
无论如何,一个refspec通常只是其中两个,中间有一个冒号:
:
refs/heads/master:refs/heads/master
例如。您需要其中两个,因为您正在使用两个Git:一个在您的系统上,您要求它做一些事情,另一个在一些远程上,如gitlab
或github
。你让你的Git通过互联网电话调用另一个Git。然后你的Git和他们的Git相互交谈,然后你的Git会获取或推送东西。
获取和推送步骤需要为每个Git提供一个引用,这意味着您需要两个引用:一个refspec。
refspec的两半是源和目标。如果您正在运行git fetch
,那么源代码是其他Git,目标代码是您自己的Git。如果您正在运行git push
,您就是源;另一个Git成为目的地。但在任何一种情况下,源都会向目标提交一些提交,然后源的名称(refspec的左半部分)被用来更改目标的name中的某些内容,即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
,而是他们的master
——设置为建立在他们以前提交基础上的最新提交。
我们确保它建立在他们的基础上,先获取,然后工作,然后推动。如果我们在与索菲亚的"比赛"中失利——我们都获得了大约相同的时间,但她工作得更快,然后她在我们之前推动——我们会得到"远程拒绝"不快进"的错误;我们必须再次获取,从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
的分支,但您的Git为的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
命令也会尝试猜测这会做什么。)