git - 'git pull 是如何 ' 工作?
问题描述
我对git-pull
文档的这一部分有点困惑:
$ git pull origin next
next
这会暂时留下 的副本FETCH_HEAD
,但不会更新任何远程跟踪分支。
这种形式的git pull
实际是否从远程存储库中获取新的提交到本地存储库?
解决方案
TL;博士
非常简短的回答是肯定的:git fetch
获取他们的提交并将它们放入您的存储库。但是git pull
这里的文档是错误的,以一种小而重要的方式。
长
我建议避免 git pull
,至少在您非常熟悉它运行的两个命令的内部工作之前。相反,请使用两个单独的命令:git fetch
, 然后——假设你打算合并——<code>git merge。然而,这确实值得一些解释。特别是,声称这
不更新任何远程跟踪分支
通常是错误的,尽管在某些特定情况下它可能是正确的。这对于 Git 1.8.2 之前的 Git 版本是正确的。
如文档所述,git pull
运行git fetch
,然后运行第二个 Git 命令,通常是git merge
. 第一个 (fetch) 命令获取您传递给 的大部分选项和参数git pull
,尽管有一些选项被带走并传递给git merge
or git rebase
,甚至直接被git pull
自己使用。
在以下具体情况下:
git pull origin next
这运行:
git fetch origin next
它指向git fetch
:
git config --get remote.origin.url
在;结果处调用另一个 Git- 当该 Git 列出其所有分支和标签时,仅从其
next
(分支或标签,通常是分支)中获取;和 - 禁止更新大多数远程跟踪名称(Git 倾向于调用这些远程跟踪分支名称,但由于它们在某种重要意义上不是分支,所以我喜欢使用较短的短语remote-tracking names)。
这里的文档是指这三项中的最后一项。然而,从 Git 版本 1.8.2 开始,Git 现在执行 Git 文档所称的“机会性更新”:如果 Git 带来了origin
's 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
是它带来了提交。具体来说,它会带来Git 在其存储库中具有哈希 ID 的提交,而您的Git 在您的存储库中没有这些提交。Git 使用一些图论来确定哪些提交以及哪些其他对象需要从它们的存储库带到你的存储库,这取决于你告诉git fetch
要获取的名称。正常的默认值是所有分支名称和一些标签名称。
这些对象——主要是提交和伴随它们的文件——都由这些哈希 ID 标识。这是 Git 在这里关心的哈希 ID。您要么拥有具有哈希 ID 的东西,要么没有;如果你不这样做,git fetch
把它带过来。
一旦你有了对象,你的 Git 就会保留它们,只要有对象的名字,或者对象可以从有名字的东西中访问。这样做的过程类似于许多现代语言中的垃圾收集,例如 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 收集器的影响。
(请注意,有一个选项,-a
或者--append
,git fetch
告诉它附加到FETCH_HEAD
文件中。但是,这可能会与运行混淆git merge
,git pull
因为现在可能有多行未标记not-for-merge
。所以-a
与git pull
-you一起使用很少是一个好主意真的需要确切地知道你在这里做什么。)
推荐阅读
- javascript - XHR 请求后 window.onload 会触发吗
- adfs2.0 - Shibboleth sp 登录后联合到 ad fs
- c++ - 无法在 Geany IDE 中正确编写 #include 语句
- c - 如何在 C 中并行发送/接收包含 MPI 数组的嵌套结构?
- android - 错误:新创建的守护进程具有与预期不同的上下文
- python - 如何让我的不和谐机器人在 py 中提及没有 ID 的人?
- spring-boot - 捕获服务器的异常不可用
- amazon-web-services - 如何从阶跃函数获取输出结果
- minikube - 更新 OSx Big Mister 后无法启动 mini kube
- javascript - TypeError:无法读取未定义的属性“原型”。为什么我运行 Browserify 后会出现此错误?