【问题标题】:How does 'git pull <remote> <branchname>' work?'git pull <remote> <branchname>' 是如何工作的?
【发布时间】:2018-06-16 21:43:58
【问题描述】:

我对@9​​87654321@ 文档的这一部分有点困惑:

$ git pull origin next

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

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

【问题讨论】:

  • 确实如此(并且origin/next remote 现在指向远程分支的尖端),只是您的本地分支需要合并/重新定位以显示新内容。

标签: git git-pull


【解决方案1】:

TL;DR

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

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

不更新任何远程跟踪分支

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

如文档所述,git pull 运行 git fetch,然后运行第二个 Git 命令,通常是 git merge。第一个(获取)命令获取您传递给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 文档所称的“机会性更新”:如果 Git 带来了 originnext,Git 会更新您自己的 origin/next即使它不会更新任何其他远程跟踪名称1在所有情况下,git fetch 都会将哈希 ID 和分支或标签名称写入.git/FETCH_HEAD( s) 它带来了。

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


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

当然,如果您使用 URL 而不是远程名称,Git 就无法知道机会性地更新哪些名称。所以在 this 的情况下——当你运行时:

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

例如 - 没有要更新的远程跟踪名称,无论您是否向命令添加额外的 refspec。


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

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

这些对象——主要是提交和伴随它们的文件——都由这些哈希 ID 标识。这是 Git 在这里关心的哈希 ID。您要么拥有具有哈希 ID 的东西,要么没有;如果你不这样做,git fetch 带来它。

一旦你有了对象,你的 Git 就会保留它们,只要 something 有对象的 name,或者对象是 可从 有名字的东西。这样做的过程类似于许多现代语言中的垃圾收集,例如 Java、Python 和 Go(以及更古老的语言,例如 Lisp 及其大多数后代)。本质上,给定一些起点,Git 会遍历通过读取提交2 形成的图表,以获取它们的父哈希 ID。在此图遍历期间到达的任何提交或其他对象都被引用并保留;任何提交或到达的其他对象都未被引用并且有资格被删除。

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


2此描述省略了注释标签 对象。这些也包含哈希 ID 并导致目标对象被引用(如果它是标记、提交或树对象,则遍历)。请注意,每个提交都包含一个树对象的哈希 ID。垃圾收集代码必须遍历这个包含更多对象 ID 的树对象,以将这些对象标记为已引用,并且当然递归地遍历树中的任何子树。幸运的是,除了 tree 和 blob 对象,或者偶尔特殊的 gitlink 之外,trees 不能引用任何东西,它是从子模块中获取的哈希 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]

由于您运行的下一个 git fetch覆盖 FETCH_HEAD 文件,如果没有远程跟踪名称记住此哈希 ID,则未来的 git fetch可以使对象61856a...d5f2f 有资格进行垃圾收集。但是,如果您已将其合并到您自己的分支中,您的分支名称指向一个提交,当遵循提交图时,最终指向61856a...d5f2f,即——通过引用它——保护来自 Grim Collector 的提交。

(请注意,git fetch 有一个选项-a--append 告诉它追加FETCH_HEAD 文件。但是,这可能会与@ 混淆git pull 运行的 987654371@,因为现在可能有多行标记为 not-for-merge。因此,将 -agit pull 一起使用并不是一个好主意——你真的需要确切地知道什么你在这里做。)

【讨论】:

    猜你喜欢
    • 2021-08-03
    • 2013-11-06
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-21
    • 2023-02-26
    • 2011-01-26
    相关资源
    最近更新 更多