Git: "git pull"到底做了什么?

标签 git

我知道git pull实际上是git fetch的组合和 git merge ,并且它基本上像在远程存储库中一样引入存储库。

  • 但是,这是否意味着在 git pull 之后?我的工作树将与远程仓库相同吗?
  • 我发现一些案例做 git pull不会更改我本地存储库中的任何内容或创建任何新提交。对此有何解释?
  • git pull有意义吗?仅在索引处进行更改?
  • 如果是这样,如何使索引处的更改向前移动到工作树?
  • 最佳答案

    确切的部分真的很艰难。人们常说——而且大部分是真的——git pull运行 git fetch其次是 git mergegit 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 mergegit 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/

    相关文章:

    windows - 远程控制git仓库

    git - git commit占用了多少磁盘空间

    git - 如何在推送后显示 Visual Studio git 中的所有更改

    Git Gui 错误 - 无法暂存所选行......补丁不适用

    git - 是否可以使用 SSH 协议(protocol)在 gitlab.com 上镜像私有(private)存储库?

    git - 如何使用命令行删除我在 Git 上的存储库?

    git - GitHub for Windows 可以与 GitLab 一起使用吗?

    git - 推送克隆的存储库时出错

    git - Sourcetree 中的 .gitignore 文件不起作用

    GIT:将提交日期更改为作者日期