'git pull <remote> <branchname>'是如何工作的?

时间:2018-06-16 21:43:59

标签: git git-pull

我对git-pull文档的这一部分感到有点困惑:

$ git pull origin next
     

这会在next暂时留下FETCH_HEAD的副本,但不会   更新任何远程跟踪分支。

这种形式的git pull实际上是否会从远程存储库获取新的提交到本地提交?

1 个答案:

答案 0 :(得分:0)

TL; DR

非常简短的回答是肯定的:git fetch获取他们的提交并将它们放入您的存储库。但git pull文档在这里是错误的,只是一个小而重要的方式。

我建议避免 git pull,至少在您熟悉它运行的两个命令的内部工作方式之前。相反,使用两个单独的命令:git fetch,然后 - 假设您打算合并 - git merge。但是,这确实值得一些解释。特别是声称这个

  

不会更新任何远程跟踪分支

通常是错误的,尽管在某些特定情况下它可能是正确的。对于Git 1.8.2之前的Git版本, 是正确的。

如文档所述,git pull运行git fetch,然后运行第二个Git命令,通常为git merge。第一个(fetch)命令获取传递给git pull的大多数选项和参数,尽管有一些选项被删除并传递给git mergegit rebase,甚至直接使用由git pull本身。

在具体案例中:

git pull origin next

这运行:

git fetch origin next

指示git fetch

  • git config --get remote.origin.url;
  • 的结果调出另一个Git
  • 当Git列出其所有分支和标签时,只从其next(分支或标签,通常是分支)获取;和
  • 禁止更新大多数远程跟踪名称(Git倾向于称这些远程跟踪分支名称,但由于他们在一个重要意义上不是分支,我喜欢使用较短的短语远程跟踪名称)。

这里的文档是指这三个项目中的最后一个。但是,自Git版本1.8.2以来,Git现在执行Git文档调用的内容和#34;机会主义更新&#34;:如果Git带来了origin next,Git更新您自己的origin/next,即使它没有更新任何其他远程跟踪名称 1 在所有情况下,git fetch都会写入进入.git/FETCH_HEAD哈希ID以及它带来的分支或标签的名称。

git merge运行的后续git pull使用此FETCH_HEAD文件中的哈希ID,以及行上的消息不标记为not-for-merge。请参阅以下有关FETCH_HEAD文件内容的部分。

1 这种机会更新的机制特别曲折:Git读取git config --get remote.origin.fetch的值,以了解如何重命名其他上的分支名称 Git在您的 Git存储库中将它们转换为远程跟踪名称时。如果您将此设置为其正常默认值之外的其他内容,或者如果没有设置,则即使在Git版本1.8.2或更高版本中,文档的声明也可能正确。

当然,如果您使用URL而不是远程名称,Git无法知道要更新哪些名称。所以在这个的情况下 - 当你运行时:

git fetch https://example.com/repo.git
例如

- 无论是否向命令添加其他refspec,都不会更新远程跟踪名称。

提交哈希ID,或git fetch带来的

git fetch真正重要的部分是它带来了提交。具体来说,它会在其存储库中引入其哈希ID Git的提交,您的 Git在您的存储库中没有。 Git使用一些图论来根据你告诉git fetch要获取的名称来确定需要从他们的存储库中提交什么提交以及其他什么对象。正常的默认值是所有分支名称和一些标记名称

这些对象 - 主要是提交以及随之而来的文件 - 都由这些哈希ID标识。这是Git关注的哈希ID。你要么拥有具有哈希ID的东西,要么就是你没有;如果你不这样做,git fetch会把它带过来。

一旦你拥有了这些对象,只要某些东西具有该对象的名称,或者该对象可以从到达,你的Git就会保留它们>有名字的东西。执行此操作的过程类似于Java,Python和Go等许多现代语言中的垃圾收集(以及许多较旧的语言,如Lisp及其大多数后代)。基本上,给定一些起点,Git遍历通过读取提交 2 形成的图形来获取它们的父哈希ID。在此图形遍历期间到达的任何提交或其他对象都引用并保留; 到达的任何提交或其他对象都未被引用,并且有资格删除。

此处FETCH_HEAD文件的内容计数:该文件中各行上的任何哈希ID都是引用的对象。因此,哈希ID的存在会保留对象,即使origin/next永远不会更新(Git版本早于1.8.2,或脚注1中其他特殊但不太可能的条件之一)。

2 此说明省略了带注释的标记对象。这些也包含哈希ID并导致目标对象被引用(如果它是标记,提交或树对象则遍历)。请注意,每个提交都包含一个树对象的哈希ID。垃圾收集代码必须遍历此树对象,该对象包含更多对象ID,以将这些对象标记为已引用,当然还以递归方式遍历树中的任何子树。幸运的是,树不能引用除树和blob对象之外的任何东西,或偶尔使用的特殊gitlink,它是从子模块中获取的哈希ID,但不会在此处遍历。

关于FETCH_HEAD

查看FETCH_HEAD文件 - 它的纯文本,易于阅读 - 你会发现类似这样的内容:

3e5524907b43337e82a24afbc822078daf7a868f                branch 'master' of [url]
fc54c1af3ec09bab8b8ea09768c2da4069b7f53e        not-for-merge   branch 'maint' of [url]
61856ae69a2ceb241a90e47953e18f218e4d5f2f        not-for-merge   branch 'next' of [url]
fc16284eae5b5a7c4786612ba2c254f3f23b1086        not-for-merge   branch 'pu' of [url]
9125ddae1445fd35a9e52a21f926a2785a2583b8        not-for-merge   branch 'todo' of [url]

(哈希ID和名称等等当然会有所不同)。这来自git fetch,没有任何限制;一个专门针对next并且打算用于合并的内容将改为:

61856ae69a2ceb241a90e47953e18f218e4d5f2f                branch 'next' of [url]

由于您运行的 next git fetch覆盖 FETCH_HEAD文件,如果没有远程跟踪名称记住此哈希ID, future git fetch可以使对象61856a...d5f2f符合垃圾回收的条件。但是,如果您已将合并到您自己的分支中,您的分支名称指向一个提交,当通过提交图表后,最终指向{{1通过引用它来保护Grim Collector的提交。

(请注意,61856a...d5f2f有一个选项-a--append,可以将追加告诉git fetch文件。但是,这可能会导致FETCH_HEAD git merge运行,因为现在可能有多行而不是标记为git pull。所以它很少有用将not-for-merge-a一起使用的想法 - 你真的需要知道你在这里做了什么。)