为什么git fetch origin master:master快进本地分支?

时间:2019-06-16 04:14:19

标签: git version-control git-fetch

我一直在玩git并坚持使用git fetch的概念。

我尝试并观察了以下情形:

1) $ git fetch

将提取所有远程分支,并更新所有相应的远程跟踪分支。我知道它查看.git / config文件,并使用默认的远程存储库位置和默认的获取refspec,通常为“ refs / heads / :refs / remotes / origin / ”。它不会快速转发任何本地分支。

2) $ git fetch origin

与1相同)

3) $ git fetch origin master

仅提取master分支,并更新远程跟踪分支的原始/ master。不快进本地的“ master”分支。

4) $ git fetch origin master:master

仅提取master分支,并更新远程跟踪分支的原始/ master。快进本地的“ master”分支。

理解1)和2)很简单。但是3)和4)使我感到困惑。

fetch命令对本地分支有什么作用?为什么它会快速转发本地分支? (证明它会影响本地分支的是我尝试git fetch origin master:master时,检出master分支时会引发以下错误:fatal: Refusing to fetch into current branch refs/heads/master of non-bare repository

根据我的直觉,在任何类型的fetch命令上,更新远程跟踪分支都是默认行为。但是,执行git fetch origin master时,它具有更新FETCH_HEAD的其他行为。执行git fetch origin master:master时,它具有更新本地分支“ master”的其他行为。我的直觉对吗?

它会将git fetch origin master:master解释为git fetch origin refs/heads/master:refs/heads/master吗? (也许对我之前的问题是多余的)

它会将git fetch origin master解释为git fetch origin refs/heads/master:吗?

2 个答案:

答案 0 :(得分:2)

<refspec>中明确提及目标时,更新匹配的本地参考 是预期的行为。

格式:

git fetch [options] <remote> <refspec>

refspec采用以下形式

<src>:<dst>

(显然,对于“来源”和“目的地”而言)

据说in the manual

  

已获取与<src>匹配的远程引用,并且如果<dst>不是空字符串,则会尝试更新与之匹配的本地引用。

您还可以在此特定语法上方查看this paragraph


并回答附带的问题:

  

但是,当执行git fetch origin master时,它具有更新FETCH_HEAD的其他行为。执行git fetch origin master:master时,它具有更新本地分支“ master”的其他行为。我的直觉对吗?

不完全是。如果未将<refspec>用作<src>:<dst>,则将给定名称用作<src>,所以当您执行{{1 }}与git fetch origin master相同。 git fetch origin master:master用于内部,即FETCH_HEAD的内部,并且可用于编写脚本,但是您不必费心每天使用git。

  

“它会将git git pull解释为fetch origin master:master吗?(也许对我之前的问题是多余的”)

是的

  

它会将git fetch origin refs/heads/master:refs/heads/master解释为git fetch origin master吗?

,如果git fetch origin refs/heads/master:的{​​{1}}面为空,则由于没有指向任何内容,因此没有任何更新。 (不要与上面提到的省略<dst>本身的情况相混淆。有趣的是,相反的<refspec>会删除:,因为它是用void ref更新的)

答案 1 :(得分:2)

RomainValeri's answer在大多数情况下是 ,但是缺少一些项目。

首先,我们必须记下有关1.8.4之前的Git版本的特殊情况。 The 1.8.4 release notes提到

  
      
  • “ git fetch origin master”不同于“ git fetch origin”或“ git fetch”   没有更新“ refs / remotes / origin / master”;这是一个早期   保持更新远程跟踪分支的设计决策   可预见的,但实际上,人们发现它的机会更多   只要有机会,就可以方便地机会性地更新它们   机会,我们在运行“ git push”时一直在更新它们   反正已经破坏了原始的“可预测性”。
  •   

因此:

git fetch origin master

在Git 1.8.3和更早版本中表现不同。在1.8.4及更高版本中,此更新refs/remotes/origin/master(默认情况下,但取决于您的默认获取refspecs)。

对refspec的描述是正确的,但缺少一项:语法通常为:

  • 表示+的可选前导加号--force(仅对于此特定refspec);
  • 来源参考;
  • 分隔符:;和
  • 目的地参考

源和目标都可以完全限定,例如refs/heads/master,例如,不可以完全限定,例如master。如果您使用不合格的变体,Git会找出可能的完全合格的拼写。

通常,可以省略源和目标中的任何一个(有时甚至是两者)。对于git fetchgit push,这种refspec的含义是不同的。省略源代码需要包含一个冒号,而省略目标代码也可以使您也省略冒号。那就是:

  • master:masterbranch1:branch2tag:tag等都提供源和目标。
  • master是没有目的地的来源。
  • master:也是没有目的地的来源。
  • :master是没有来源的目的地。这仅对git push有效,这意味着要求另一个Git删除其引用
  • :表示省略了源和目的地。这仅对git push有效,这意味着找到匹配的分支名称并将其推送

忽略git push,然后只允许源和目标,例如master:origin/mastermaster:master,或者不带源的目标,例如master或{ {1}}。因此只有两种情况:

  1. 您提供了来源,但没有目的地。 master:不会更新任何名称, 除外,因为自1.8.4起Git会机会性地更新默认获取引用规范中指定的所有远程跟踪名称。因此,如果您的Git至少为1.8.4,则git fetch更新您的git fetch origin master,这是您的origin/master的Git的origin的Git的远程跟踪名称。

  2. 您同时提供了源和目标。 master将尝试更新目的地名称。如果未设置强制标志(全局标志或前导git fetch),则此更新必须是快进的。如果您未指定+,则目的地也不能是当前分支。 1 无论如何,如果您的Git为1.8.4或更高版本,您的Git会机会性地更新遥控器。 -跟踪名称作为源名称。

请注意:

--update-head-ok

(在命令行上不指定refspecs)将使用您配置中的默认refspecs。假设这是git fetch origin ,请参见origin来查看那些默认的参考规范。


1 在我认为是个错误的情况下,此处针对 branch的测试已退出,仅测试 main 工作-tree的git config --get-all remote.origin.fetch。如果当前已在添加的工作树中检出分支,则添加的工作树的索引和工作树将不再与其分支同步。另请参见Why does Git allow pushing to a checked-out branch in an added worktree? How shall I recover?-该错误在获取和推送之间是对称的。