Git:什么确切地“git拉”做? [英] Git: What does EXACTLY "git pull" do?

查看:125
本文介绍了Git:什么确切地“git拉”做?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


  1. 但是,这是否意味着在git pull之后,我的工作树将与远程回购相同?
  2. b $ b
  3. 我发现有些情况下,git pull不会改变我本地仓库中的任何内容或创建任何新的提交?

  4. 是否合理:git pull 仅在索引处进行更改?

  5. 如果是这样,我如何使索引处的更改向前移动到工作树?


解决方案

确切地说,部分确实相当困难。我们经常说 - 大多数情况是这样的 - git pull 运行 git fetch ,后面跟着 git merge git rebase ,实际上, git fetch ,曾经是一个shell脚本,现在是一个C程序,它的字面意思是首先运行 git fetch ,但现在它直接调用实现 git fetch



然而,下一步很棘手。此外,您在评论中添加了以下内容:


[fetch]带来了远程回购的变化。

为了正确理解这一点,您必须了解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
提交者Junio C Hamano< gitster pobox.com> 1494339962 +0900

Git 2.13

签名:Junio C Hamano< gitster pobox.com>

如果您提供这些内容(减去的/ @ / /' 部分,但是Git添加到每个对象的头部)到SHA-1校验和计算器,您将获得哈希ID。这意味着拥有此提交的 everyone 具有相同的哈希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 ... - 自己在某处,还有它的对象和需要的其他对象。这些是在 git fetch git push期间看到Git计数和压缩的对象



行会告诉哪个提交(或者对于合并,提交,复数)是这个特定承诺的前身。为了拥有一组完整的提交,Git还必须拥有父提交( - shallow clone)可以故意忽略各种父代,其中的ID被记录在一个浅移植的特殊文件中,但是一个普通的克隆将总是拥有一切)。

总共有四种类型的对象:提交, (带注释的)标签,树和Git调用的对象。 Blob主要存储实际的文件。所有这些对象都驻留在Git的对象数据库中。例如,Git可以通过哈希ID轻松地检索它们: git cat-file -p< hash> ,以一种模糊的人类可读格式显示它们。 (大多数情况下,除了解压缩之外,除了树形对象都有二进制数据,必须首先进行格式化,否则除了解压缩外,几乎没有什么必须完成的。)



运行 git fetch - 或者 git pull 为您运行它 - 您的Git从另一个Git获取一些初始对象的哈希ID ,然后使用Git传输协议来确定完成Git存储库需要哪些其他对象。如果你已经有一些对象,你不需要再次获取它,如果这个对象是一个提交对象,你不需要它的任何父对象。 1 所以你只能得到你还没有的提交(和树和斑点)。



一旦这些对象被安全地保存起来,Git会在特殊的 FETCH_HEAD中记录哈希ID 文件。如果你的Git至少是1.8.4,它现在也会 > origin / master



(如果您手动运行 git fetch 服从所有正常的refspec更新规则,如 git fetch 文档。它是通过 git传递给 git fetch 的附加参数拉,这些取决于你的Git版本。)



那么,这就是我认为你的真实的答案第一个问题: git fetch 将这些对象存储在Git的对象数据库中,它们可以通过它们的散列ID进行检索。它将散列ID添加到 .git / FETCH_HEAD (总是),并且通常也会更新 refs / tags / <中的某些引用标签名称以及 refs / remotes / 中的远程追踪分行名称。




1 除此之外,也就是对一个浅层克隆进行unshallow。




其余的 git pull



运行 git fetch 可以获取对象,但不会将这些对象合并到任何 工作中。如果您希望使用提取的提交或其他数据,则需要执行第二步。

这里可以执行的两个主要操作是 git merge git rebase 。了解它们的最好方法是在别处阅读它们(其他SO帖子,其他文档等)。然而,这两者都是复杂的命令 - 并且这两者包含 >的特殊情况:特别是,您可以 git pull 到一个不存在的分支中。在两种情况下,您有一个不存在的分支(Git也称为孤儿分支未分娩分支):

在运行 git checkout --orphan newbranch 之后,在一个新的空的仓库(没有提交)或
  • / code>



  • 在这两种情况下,没有当前提交重新整合或合并。但是,索引和/或工作树不一定是空的!它们最初在一个新的空仓库中是空的,但是当你运行 git pull 时,你可以创建文件并将它们复制到索引中。



    这种 git pull 传统上是bug,所以要小心:在1.8-ish之前的版本Git有时会破坏未提交的工作。我认为最好避免 git pull 完全在这里:自己运行 git fetch ,然后找出你想要的去做。据我所知,在现代Git中可以,这些版本不会破坏你的索引和工作树,但是我习惯于避免使用 git pull 。 / p>

    在任何情况下,即使您不在孤儿/未出生/不存在的分支上,尝试运行 git也不是一个好主意合并与脏索引和/或工作树(未提交的工作)。现在 git rebase 命令有一个自动存储选项( rebase.autoStash ),所以你可以让Git自动运行 git stash save 来创建任何这样未提交的工作的一些分支提交。然后,rebase本身可以运行,之后Git可以自动应用并删除存储。


    $ b

    git merge 命令没有这个自动选项,但当然你可以手动完成。



    请注意,如果您处于冲突<合并。在这种状态下,索引有额外的条目:在解决冲突之前,你不能提交这些条目,甚至不能存储它们(这自然地来自于 git stash 真的存在提交)。你可以在任意时间运行 git fetch ,因为它只是将新对象添加到对象数据库;但当索引处于此状态时,您无法合并或重新绑定。


    I know that the "git pull" is actually a combination of "git fetch" and "git merge" and that basically it brings the repository as it it in remote repository.

    1. But still, does it mean that after "git pull" my working tree will be identical to the remote repo?
    2. I found some cases that doing "git pull" doesn't change anything in my local repo or create any new commit?
    3. Does it make sense that "git pull" makes changes at the index only?
    4. If it does, how can I make the changes at index move forward to the work tree?

    解决方案

    The exactly part is really quite tough. It's often said—and it's mostly true—that git pull runs git fetch followed by either git merge or git rebase, and in fact, git fetch, which used to be a shell script and is now a C program, quite literally ran git fetch first, though now it directly invokes the C code that implements git fetch.

    The next step, however, is quite tricky. Also, in a comment, you added this:

    [fetch] brings changes from the remote repo. Where does it put them?

    To understand this properly, you must understand Git's object system.

    The Git object model, and git fetch

    Each commit is a sort of standalone entity. Every commit has a unique hash ID: b06d364... or whatever. That hash ID is a cryptographic checksum of the contents of that commit. Consider, for instance:

    $ 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>
    

    If you feed these contents (minus the 's/@/ /' part but with the header that Git adds to every object) to a SHA-1 checksum calculator, you will get the hash ID. This means that everyone who has this commit has the same hash ID for it.

    You can get the Git repository for Git and run git cat-file -p v2.13.0^{commit} to see this same data. Note: the tag v2.13.0 translates to 074ffb61b4b507b3bde7dcf6006e5660a0430860, which is a tag object; the tag object itself refers to the commit b06d364...:

    $ git cat-file -p v2.13.0
    object b06d3643105c8758ed019125a4399cb7efdcce2c
    type commit
    tag v2.13.0
    [snip]
    

    To work with a commit, Git must store the commit object—the item with the hash ID b06d364...—itself somewhere, and also its tree object and any additional objects that tree needs. These are the objects that you see Git counting and compressing during a git fetch or git push.

    The parent line tells which commit (or, for a merge, commits, plural) are the predecessors of this particular commit. To have a complete set of commits, Git must also have the parent commit(s) (a --shallow clone can deliberately omit various parents, whose IDs are recorded in a special file of "shallow grafts", but a normal clone will always have everything).

    There are four types of object in total: commits, (annotated) tags, trees, and what Git calls blob objects. Blobs mostly store the actual files. All of these objects reside in Git's object database. Git can then retrieve them easily by hash ID: git cat-file -p <hash>, for instance, displays them in a vaguely human-readable format. (Most of the time there is little that must be done other than de-compressing, though tree objects have binary data that must be formatted first.)

    When you run git fetch—or have git pull run it for you—your Git obtains the hash IDs of some initial objects from another Git, then uses the Git transfer protocols to figure out what additional objects are required to complete your Git repository. If you already have some object, you do not need to fetch it again, and if that object is a commit object, you do not need any of its parents either.1 So you get only the commits (and trees and blobs) that you do not already have. Your Git then stuffs these into your repository's object database.

    Once the objects are safely saved away, your Git records the hash IDs in the special FETCH_HEAD file. If your Git is at least 1.8.4, it will also update any corresponding remote-tracking branch names at this time: e.g., it may update your origin/master.

    (If you run git fetch manually, your Git obeys all the normal refspec update rules, as described in the git fetch documentation. It's the additional arguments passed to git fetch by git pull that inhibit some of these, depending on your Git version.)

    That, then, is the answer to what I think is your real first question: git fetch stores these objects in Git's object database, where they may be retrieved by their hash IDs. It adds the hash IDs to .git/FETCH_HEAD (always), and often also updates some of your references—tag names in refs/tags/, and remote-tracking branch names in refs/remotes/.


    1Except, that is, to "unshallow" a shallow clone.


    The rest of git pull

    Running git fetch gets you objects, but does nothing to incorporate those objects into any of your work. If you wish to use the fetched commits or other data, you need a second step.

    The two main actions you can do here are git merge or git rebase. The best way to understand them is to read about them elsewhere (other SO postings, other documentation, and so on). Both are, however, complicated commands—and there is one special case for git pull that is not covered by those two: in particular, you can git pull into a non-existent branch. You have a non-existent branch (which Git also calls an orphan branch or an unborn branch) in two cases:

    • in a new, empty repository (that has no commits), or
    • after running git checkout --orphan newbranch

    In both cases, there is no current commit so there is nothing to rebase or merge. However, the index and/or work-tree are not necessarily empty! They are initially empty in a new, empty repository, but by the time you run git pull you could have created files and copied them into the index.

    This kind of git pull has traditionally been buggy, so be careful: versions of Git before 1.8-ish will sometimes destroy uncommitted work. I think it's best to avoid git pull entirely here: just run git fetch yourself, and then figure out what you want to do. As far as I know, it's OK in modern Git—these versions will not destroy your index and work-tree—but I am in the habit of avoiding git pull myself.

    In any case, even if you are not on an orphan/unborn/non-existent branch, it's not a great idea to try to run git merge with a dirty index and/or work-tree ("uncommitted work"). The git rebase command now has an automatic-stash option (rebase.autoStash), so you can have Git automatically run git stash save to create some off-branch commits out of any such uncommitted work. Then the rebase itself can run, after which Git can automatically apply and drop the stash.

    The git merge command does not have this automatic option, but of course you can do it manually.

    Note that none of this works if you are in the middle of a conflicted merge. In this state, the index has extra entries: you cannot commit these until you resolve the conflicts, and you cannot even stash them (which follows naturally from the fact that git stash really makes commits). You can run git fetch, at any time, since that just adds new objects to the object database; but you cannot merge or rebase when the index is in this state.

    这篇关于Git:什么确切地“git拉”做?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆