(恐怕这个答案再次相当长,因为这些问题做出的假设至少在今天是不正确的。跳到粗体部分以获取实际答案,但之前的所有内容也很重要。)
您引用的书籍文本已经过时,并且(现在)完全是错误的(这是不断发展的软件的危险,书籍已经过时):
首先,如果没有为命令提供显式远程,Git 会假设您想要使用 origin。
在相对快速地浏览 git 自己的 git 历史记录时,我还没有准确地找到behavior改变了,但是文档在 git 1.8.2.1 中进行了更改,并且now says:
当命令行没有指定推送到哪里时<repository>
争论,branch.*.remote
配置为
咨询当前分支以确定推送到哪里。如果
缺少配置,默认为origin.
(此行为符合git fetch
也不足为奇,因为他们使用相同的源代码来获得该结果。)这没有提到可选的branch.$branch.pushremote
设置,它会覆盖branch.$branch.remote
。在所有情况下都在这里$branch
代表您当前的分支,如输出所示git symbolic-ref --short HEAD
。 (如果您处于“分离头”模式,那么git symbolic-ref
fails, git push
通常会给你一个“致命的:你当前不在分支上”错误,我认为从 2.0 版本开始,默认值发生了变化。)
This is still不是故事的全部;git-config 文档又增加了一个怪癖:
remote.pushDefault
默认情况下要推送到的遥控器。覆盖branch.<name>.remote
对于所有分支,并且被覆盖branch.<name>.pushRemote
为了
具体分支机构。
(幸运的是,从今天开始,我think这涵盖了一切。也就是说,git 首先寻找pushremote
当前分支的设置。如果没有这样的设置,它接下来会寻找remote.pushdefault
。如果也没有设置,它将依赖于remote
当前分支的设置。旧版本 git 中的行为可能有所不同。另请注意,配置项不区分大小写:pushremote
and pushRemote
是相同的配置名称[这让我想知道分支名称部分是否也不区分大小写;我得找个时间测试一下]。)
关于 refspecs 的下一部分甚至更加错误,因为 git 随着时间的推移已经发生了一些变化,其中一些甚至在 git 的内置文档中也没有很好的记录。你的引述如下:
如果没有 refspec,git Push 会将您的提交发送到远程,以获取您的存储库和上游存储库之间常见的所有分支。
The 新文档 says:
当命令行没有指定要推送的内容时<refspec>...
论据或--all
, --mirror
, --tags
选项,该命令发现
默认值<refspec>
通过咨询remote.*.push
配置,
如果没有找到,荣誉push.default
配置来决定
要推送什么(参见git 配置的意义push.default
).
The push.default
设置是在 git 1.8 版本左右添加的,但它是默认的valuegit 2.0 中发生了变化。如果您还没有配置push.default
你会收到这个警告:
warning: push.default is unset; ...
自从配置项可用以来。咨询git-config 文档,你现在会发现这个:
push.default
定义动作git push
如果没有明确给出参考规范,则应采用。不同的值非常适合特定的工作流程......
有五个选项,我将在这里简单列出(有关详细信息,请参阅链接文档):nothing
, current
, simple
, upstream
, and matching
。您引用的书本文字描述了matching
,这是 git 2.0 版本之前的默认行为。新的默认值是simple
.
On git push
,如果您配置“匹配”行为
“您的存储库和上游存储库之间共有的所有分支”是否与本地存储库中的所有远程跟踪分支以及远程存储库中相应的跟踪分支相同?
No.
我建议你尝试跑步git ls-remote origin
(或替换origin
与任何其他遥控器的名称):
$ git ls-remote origin
28274d02c489f4c7e68153056e9061a46f62d7a0 HEAD
1ff88560c8d22bcdb528a6629239d638f927cb96 refs/heads/maint
28274d02c489f4c7e68153056e9061a46f62d7a0 refs/heads/master
0ac5344e619fec2068de9ab2afdb99d1af8854be refs/heads/next
5467631257c047b16d95929559dd1887d27b0ddc refs/heads/pu
68a0f56b615b61afdbd86be01a3ca63dca70edc0 refs/heads/todo
d5aef6e4d58cfe1549adef5b436f3ace984e8c86 refs/tags/gitgui-0.10.0
3d654be48f65545c4d3e35f5d3bbed5489820930 refs/tags/gitgui-0.10.0^{}
[snip -- lots more omitted]
然后,尝试git for-each-ref refs/heads
:
$ git for-each-ref refs/heads
您将看到类似的输出,但具有不同的 SHA-1,以及单词commit
夹在中间。
这里的要点是列出的每个“头”refs/heads
- 在两个输出中,从ls-remote
和来自for-each-ref
—代表(本地)分支机构git push
做“匹配”时要考虑。匹配的头被添加到推送列表中,给出一组 refspec(以内部形式,但很容易表示为name:name
或者,如果您使用--force
flag, +name:name
).
有时候是这样的即使没有为这些本地分支配置上游。因此,如果他们(遥控器)有refs/heads/bob
这是供鲍勃·罗伯茨使用的,你有一个refs/heads/bob
你用来获取有关你的管道浮子或钓鱼浮子或其他什么的信息,git 会尝试将它们匹配,即使你的bob
并不意味着要逆流而上。
On push
vs fetch
报价是为了git push
。它(特别是没有参考规格的情况下的部分)是否适用于git fetch
and to git pull
?
No.
The behavior of git fetch
is easier to explain, because it doesn't do all the weird stuff on you that git pull
does. The git pull
command is supposed to be convenient, but git pull
is trying to mash a fundamentally repository-wide "fetch" operation together with a fundamentally branch-constrained "merge" operation. These two do not play well together.1 (At best, I think git pull
could at some point be modified to do a simpler, repository-wide "fetch all, then check out and merge, pairwise, some branch(es) based on tracking configs and arguments" sequence. I think this would make it behave the way most people seem to expect it to. It would also be mostly backwards-incompatible, and behave the way it does now only for the simplest—but default—case: fetch all, then merge current branch only. But that's all speculation anyway.)
如果省略所有参考规范,git fetch
读你的remote.$remote.fetch
配置线。(请注意,这个“省略所有引用规范”子句意味着您还必须避免旧的“命名文件$GIT_DIR/branches
“例如方法。)
远程名称的默认配置行(单数)origin
通常看起来像这样(查看本地存储库配置文件以查看它):
[remote "origin"]
+refs/heads/*:refs/remotes/origin/*
这就是“远程跟踪分支”产生的实际机制。
The fetch
过程同样开始git ls-remote origin
步骤,它从远程获取每个引用的列表——分支、标签或你拥有的东西。这fetch
代码继续将它们与每个fetch =
下的行[remote "origin"]
。参考规范左侧的星号具有明显的含义;右侧的星号将替换为左侧星号匹配的内容。 (在当前版本的 git 中*
必须匹配“整个部分”,松散地定义为“斜杠之间的东西”,但在即将到来的 git 中,这个约束将被放松——尽管我不确定有什么有用的结果。)
你可能有尽可能多的fetch =
线条如你所愿。每个原始引用都会遍历每一行以获得新的(替换)引用:如果左侧匹配,则右侧提供替换。 (如果右侧缺失或为空,由于历史原因,行为会变得有点奇怪,部分是为了保持git pull
工作正常,部分原因是所需的行为在 git 1.8 左右发生了变化。)
所有这些替换的结果形成重命名的引用,并确定复制哪些引用;但每个原始引用最多只能有一个结果:您无法将“他们的”refs/heads/foo 映射到“您的”refs/remotes/origin/barand例如,您的 refs/remotes/origin/zoink。
要使存储库充当镜像,您可以使用+refs/*:refs/*
带来所有引用而不重命名(但这会破坏每个引用上的本地分支fetch
所以这只有在以下情况下才有意义--bare
).
(“修剪”——根据对方在获取或推送的启动协商期间发送的内容删除“死”引用——是单独完成的,并且仅当您设置--prune
flag.)
1It didn't start out this way. In fact, the original pull
script predated remotes and remote-tracking branches entirely. Commit 7ef76925d9c19ef74874e1735e2436e56d0c4897 split pull
into pull
and fetch
and then the two evolved in different directions over time; once "remotes" and "remote-tracking branches" came into existence, they'd evolved to the point that trying to mix them was, I think, just a bad idea, and now it's even worse. :-)