我知道git pull
实际上是git fetch
的组合和 git merge
,并且它基本上像在远程存储库中一样引入存储库。
git pull
之后?我的工作树将与远程仓库相同吗? git pull
不会更改我本地存储库中的任何内容或创建任何新提交。对此有何解释? git pull
有意义吗?仅在索引处进行更改? 最佳答案
确切的部分真的很艰难。人们常说——而且大部分是真的——git pull
运行 git fetch
其次是 git merge
或 git rebase
,事实上,git pull
,它曾经是一个 shell 脚本,现在是一个 C 程序,实际上是运行 git fetch
首先,虽然现在它直接调用实现 git fetch
的 C 代码.
然而,下一步非常棘手。此外,在评论中,您添加了以下内容:
[fetch] brings changes from the remote repo. Where does it put them?
要正确理解这一点,您必须了解 Git 的对象系统。
Git 对象模型,以及
git fetch
每个提交都是一种独立的实体。每个提交都有一个唯一的哈希 ID:
b06d364...
管他呢。该哈希 ID 是该提交内容的加密校验和。例如,考虑:$ git cat-file -p HEAD | sed 's/@/ /g'
tree a15b54eb544033f8c1ad04dd0a5278a59cc36cc9
parent 951ea7656ebb3f30e6c5e941e625a1318ac58298
author Junio C Hamano <gitster pobox.com> 1494339962 +0900
committer Junio C Hamano <gitster pobox.com> 1494339962 +0900
Git 2.13
Signed-off-by: Junio C Hamano <gitster pobox.com>
如果您将这些内容(减去
's/@/ /'
部分,但带有 Git 添加到每个对象的 header )提供给 SHA-1 校验和计算器,您将获得哈希 ID。这意味着拥有此提交的每个人都拥有相同的哈希 ID。您可以获取 Git 的 Git 存储库并运行
git cat-file -p v2.13.0^{commit}
查看相同的数据。注意:标签v2.13.0
转换为 074ffb61b4b507b3bde7dcf6006e5660a0430860
,这是一个标签对象;标签对象本身指的是提交 b06d364...
:$ git cat-file -p v2.13.0
object b06d3643105c8758ed019125a4399cb7efdcce2c
type commit
tag v2.13.0
[snip]
要处理提交,Git 必须存储提交对象——哈希 ID 为
b06d364...
的项目。 ——它自己在某处,还有它的 tree
对象和任何其他对象 tree
需要。这些是 objects
您会看到 Git 在 git fetch
期间计数和压缩或 git push
.parent
行告诉哪个提交(或者,对于 merge ,提交,复数)是这个特定提交的前辈。为了有一套完整的提交,Git 还必须有父提交(一个 --shallow
克隆可以故意省略各种父提交,其 ID 记录在“浅移植”的特殊文件中,但正常的克隆总是拥有一切)。总共有四种类型的对象:提交、(带注释的)标签、树和 Git 所谓的 blob 对象。 Blob 主要存储实际文件。所有这些对象都驻留在 Git 的对象数据库中。然后 Git 可以通过哈希 ID 轻松检索它们:
git cat-file -p <hash>
例如,以模糊的人类可读格式显示它们。 (尽管树对象具有必须首先格式化的二进制数据,但大多数情况下除了解压缩之外几乎没有什么必须做的。)当您运行时
git fetch
——或者有 git pull
为你运行它——你的 Git 从另一个 Git 获取一些初始对象的哈希 ID,然后使用 Git 传输协议(protocol)来确定完成你的 Git 存储库需要哪些额外的对象。如果您已经有某个对象,则不需要再次获取它,并且如果该对象是提交对象,则您也不需要它的任何父对象。1 因此,您只会获得您提交的提交(以及树和 blob)还没有。然后您的 Git 将这些填充到您存储库的对象数据库中。一旦对象被安全保存,您的 Git 会将哈希 ID 记录在特殊的
FETCH_HEAD
中。文件。如果你的 Git 至少是 1.8.4,它此时也会更新任何对应的远程跟踪分支名称:例如,它可能会更新你的 origin/master
.(如果您手动运行
git fetch
,您的 Git 会遵守所有正常的 refspec 更新规则,如 the git fetch
documentation 中所述。正是 git fetch
传递给 git pull
的附加参数会抑制其中的一些,具体取决于您的 Git 版本.)那么,这就是我认为您真正的第一个问题的答案:
git fetch
将这些对象存储在 Git 的对象数据库中,在那里可以通过它们的哈希 ID 检索它们。它将哈希 ID 添加到 .git/FETCH_HEAD
(总是),并且经常更新您的一些引用资料——refs/tags/
中的标签名称, 以及 refs/remotes/
中的远程跟踪分支名称.1除了,即“unshallow”一个浅的克隆。
其余
git pull
运行
git fetch
为您提供对象,但不会将这些对象 merge 到您的任何作品中。如果您希望使用获取的提交或其他数据,则需要第二步。您可以在此处执行的两个主要操作是
git merge
或 git rebase
.理解它们的最好方法是在别处阅读它们(其他 SO 帖子、其他文档等)。然而,两者都是复杂的命令——而且 git pull
有一种特殊情况。这两个未涵盖的:特别是,您可以 git pull
进入一个不存在的分支。在两种情况下,您有一个不存在的分支(Git 也称其为孤儿分支或未出生的分支):git checkout --orphan newbranch
在这两种情况下,都没有当前提交,因此没有什么可以 rebase 或 merge 。但是,索引和/或工作树不一定是空的!它们最初在一个新的、空的存储库中是空的,但是当您运行时
git pull
您可以创建文件并将它们复制到索引中。这种
git pull
传统上有问题,所以要小心:1.8-ish 之前的 Git 版本有时会破坏未提交的工作。我认为最好避免 git pull
完全在这里:只需运行 git fetch
自己,然后弄清楚你想做什么。据我所知,在现代 Git 中还可以——这些版本不会破坏你的索引和工作树——但我习惯于避免 git pull
我。无论如何,即使您不在孤儿/未出生/不存在的分支上,尝试运行
git merge
也不是一个好主意。带有脏索引和/或工作树(“未提交的工作”)。 git rebase
命令现在有一个自动存储选项( rebase.autoStash
),所以你可以让 Git 自动运行 git stash save
从任何此类未提交的工作中创建一些分支外提交。然后 rebase 本身可以运行,之后 Git 可以自动应用并删除存储。git merge
command 没有这个自动选项,但当然你可以手动完成。请注意,如果您处于 merge 冲突的中间,则这些都不起作用。在这种状态下,索引有额外的条目:在解决冲突之前不能提交这些条目,甚至不能存储它们(这很自然地源于
git stash
确实进行了提交的事实)。您可以运行 git fetch
,在任何时候,因为这只是向对象数据库添加新对象;但是当索引处于这种状态时,你不能 merge 或 rebase 。
关于Git: "git pull"到底做了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45137769/