很抱歉,这个答案又有点长了,因为问题所做的假设——至少今天——是错误的。请跳到粗体部分获取实际答案,但前面的所有内容也很重要。
你引用的书本文已经过时,现在是完全错误的(这是软件不断发展的危险,书本会过时)。如果没有明确指定远程命令,Git 会默认使用 origin。我快速浏览了一下 Git 的 git 历史记录,并没有找到行为更改的确切时间,但在 Git 1.8.2.1 中,文档已经发生了变化,
现在的文档如下所示:当命令行没有指定推送位置时,会查阅当前分支的 branch.*.remote 配置来确定推送位置。如果配置缺失,则默认为 origin。
(这种行为也与
git fetch
相匹配——毫不奇怪,因为它们使用同一段源代码来获得该结果。)这未提及可选的
branch.$branch.pushremote
设置,该设置会覆盖
branch.$branch.remote
。在所有这些情况下,
$branch
代表您当前的分支,如
git symbolic-ref --short HEAD
的输出所示。(如果您处于“分离头”模式,以至于
git symbolic-ref
失败,则
git push
通常会给出“fatal: You are not currently on a branch”错误,我认为自2.0版本起默认值已更改。)
这仍然没有涵盖全部内容;
git-config文档添加了另一个怪异之处:
remote.pushDefault
默认情况下要推送到的远程仓库。覆盖所有分支的 branch.<name>.remote
设置,并被特定分支的 branch.<name>.pushRemote
设置所覆盖。
(幸运的是,就今天而言,我认为这已经涵盖了所有内容。也就是说,git 首先会查找当前分支的 pushremote
设置。如果没有该设置,则接下来它将查找 remote.pushdefault
。如果也没有设置,它会回退到当前分支的 remote
设置。旧版本 git 的行为可能会有所不同。注意,配置项不区分大小写:pushremote
和 pushRemote
是相同的配置名称[这让我想知道分支名称部分是否也不区分大小写;我以后会测试一下]。)
关于 refspecs 的下一部分更加错误,这是因为 git 随着时间的推移获得了几个变化,其中一些甚至在 git 的内置文档中也没有很好地记录。你引用的内容如下:
没有指定 refspec,git push 命令会将你的提交推送到本地库和上游库之间共同存在的所有分支。根据
新文档,当命令行没有指定用
<refspec>
参数或
--all
、
--mirror
、
--tags
选项推送什么时,它会通过查找
remote.*.push
配置来找到默认的
<refspec>
,如果未找到,则遵循
push.default
配置来决定要推送什么(有关
push.default
的含义,请参见
git-config)。git 版本 1.8 左右添加了
push.default
设置,但其默认值在 git 2.0 中更改了。如果您尚未配置
push.default
,则会收到此警告:
warning: push.default is unset; ...
自从配置项可用以来。查阅
git-config文档,您现在会发现这个:
push.default
定义了如果没有显式给出refspec时git push
应该执行的操作。不同的值适合特定的工作流程...
有五个选项,我将简单列出它们(有关详细信息,请参见链接的文档):nothing
,current
,simple
,upstream
和matching
。您引用的书中的文本描述了matching
,这是git版本2.0之前的默认行为。新的默认值是simple
。
在git push
上,如果您配置了“匹配”行为
“您的存储库和上游存储库之间共同的所有分支”是否与本地存储库中的所有远程跟踪分支和远程存储库中的相应跟踪分支相同?
不是。
我建议您尝试运行
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
中列出的每个“head” - 在来自
ls-remote
和
for-each-ref
的两个输出中都是如此 - 代表了git push在进行“matching”时考虑的(本地)分支。匹配的头将添加到推送列表中,给出一组refspecs(以内部形式表示,但可以轻松表达为
name:name
或者,如果使用了
--force
标记,则
+name:name
)。
即使这些本地分支没有配置upstream也会发生这种情况。因此,如果他们(远程)有一个
refs/heads/bob
供Bob Roberts使用,而您有一个
refs/heads/bob
用于了解您的管道卡扣或钓鱼浮标等信息,git将尝试匹配它们,即使您的
bob
没有上游流。
关于push
和fetch
这段引用是针对git push
的。它(特别是没有refspec的情况下)是否适用于git fetch
和git pull
?
不适用。
git fetch
的行为更容易解释,因为它不会像
git pull
那样对你进行所有奇怪的操作。
git pull
命令应该是方便的,但是
git pull
试图将一个基本上是针对整个存储库的“获取”操作与一个基本上是针对分支限制的“合并”操作融合在一起。 这两者不能很好地协作
1。(最好的情况下,我认为
git pull
可以在某些时候被修改为执行一个更简单的、针对整个存储库的“获取所有内容,然后根据跟踪配置和参数成对地检出和合并一些分支”的顺序。 我认为这将使其表现出大多数人所期望的方式。 它也将在很大程度上不兼容,并且仅对最简单的情况(默认情况):获取所有内容,然后仅合并当前分支。 但这只是猜测。)
如果省略所有refspecs,
git fetch
将读取您的
remote.$remote.fetch
配置行。(请注意,“省略所有refspecs”子句意味着您还必须避免使用旧的“命名文件在
$GIT_DIR/branches
”方法,例如。)通常情况下,名为
origin
的远程仓库的默认配置行(单数形式)如下所示(查看本地存储库配置文件即可看到)。
[remote "origin"]
+refs/heads/*:refs/remotes/origin/*
这是“远程跟踪分支”产生的实际机制。
fetch
过程始于同样的
git ls-remote origin
步骤,获取来自远程仓库的每个引用(分支、标签等)。
fetch
代码接着将其与
[remote "origin"]
下的每个
fetch =
行进行匹配。左侧 refspec 上的星号有明显的含义;右侧的星号则被替换为左侧星号所匹配的内容。(在当前版本的 git 中,
*
必须匹配“整个部分”,宽泛地定义为“斜杠之间的内容”,但在即将推出的 git 版本中,此限制将得到放宽——尽管我不确定能带来什么有用的结果。)
你可以拥有任意数量的
fetch =
行。每个原始引用都会通过每行进行运行,以获取新的(替换后的)引用:如果左侧匹配,则右侧提供替换内容。(如果右侧缺失或为空,则由于历史原因和保持
git pull
的工作,行为会变得有些奇怪,部分原因是自 git 1.8 左右的期望行为发生了改变。)
所有这些替换的结果形成了重命名后的引用,并确定哪些引用被复制; 但是每个原始引用最多只能有一个结果:例如,您不能将"their" refs/heads/foo映射到"your" refs/remotes/origin/bar和your refs/remotes/origin/zoink。要使存储库充当镜像,请使用+refs/*:refs/*,以不重命名的方式带入所有引用(但这将在每次获取时覆盖您的本地分支,因此只有在--bare的情况下才有意义)。(基于启动协商期间其他方发送的内容删除“死”引用——通过设置--prune标志单独完成。)
1它并非一开始就是这样。事实上,最初的pull
脚本早于远程和远程跟踪分支的出现。提交7ef76925d9c19ef74874e1735e2436e56d0c4897
将pull
拆分为pull
和fetch
,然后两者随着时间的推移朝不同的方向发展。一旦“远程”和“远程跟踪分支”出现,它们已经发展到混合它们只会带来麻烦的地步,现在更加糟糕了。 :-)
origin
”:我认为这在某个时候是正确的,但那是相当久以前的事了。现在的Git默认使用branch.<branch>.remote
,除非(自1.8.3版本以来)你设置了branch.<branch>.pushremote
和/或remote.pushdefault
。 - torek