Git:确实“git pull”有什么作用?

时间:2017-07-17 06:56:15

标签: git

我知道“git pull”实际上是“git fetch”和“git merge”的组合,基本上它将存储库与远程存储库中的存储库相结合。

  1. 但是,这仍然意味着在“git pull”之后我的工作树将与远程回购相同吗?
  2. 我发现有些情况下“git pull”不会改变我本地仓库中的任何内容或创建任何新提交?
  3. “git pull”仅在索引处进行更改是否有意义?
  4. 如果是,我怎样才能使索引的更改继续前进到工作树?

5 个答案:

答案 0 :(得分:4)

完全部分真的很难。它经常被说 - 而且大部分都是正确的 - git pull运行git fetch后跟git mergegit rebase,事实上,{git fetch 1}},曾经是一个shell脚本,现在是一个C程序,首先运行git fetch,但现在它直接调用实现git fetch的C代码。

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

  

[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
committer Junio C Hamano <gitster pobox.com> 1494339962 +0900

Git 2.13

Signed-off-by: Junio C Hamano <gitster pobox.com>

如果您将这些内容(减去's/@/ /'部分,但Git添加到每个对象的标头)提供给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必须存储提交对象 - 具有哈希标识b06d364...的项目 - 本身,以及其tree对象和tree的任何其他对象需要。这些是您在objectsgit fetch期间看到Git计数和压缩的git push

parent行告诉哪个提交(或者,对于合并,提交,复数)是此特定提交的前任。要拥有一组完整的提交,Git必须拥有父提交(--shallow克隆可以故意省略各种父母,其ID记录在&#的特殊文件中34;浅层移植物&#34;,但正常的克隆将始终拥有一切)。

总共有四种类型的对象:提交,(注释)标记,树和Git调用 blob 对象。 Blob主要存储实际文件。所有这些对象都驻留在Git的对象数据库中。然后,Git可以通过哈希ID轻松检索它们:git cat-file -p <hash>,例如,以模糊的人类可读格式显示它们。 (除了解压缩之外,大部分时间都没有什么必须完成的,尽管树对象具有必须首先格式化的二进制数据。)

当你运行git fetch - 或让git pull为你运行它时 - 你的Git从另一个Git获取一些初始对象的哈希ID,然后使用Git传输协议来找出其他对象需要完成您的Git存储库。如果已经某个对象,则不需要再次获取它,如果该对象是提交对象,则不需要任何父对象。 1 因此,您只获得了您尚未拥有的提交(以及树和blob)。然后,您的Git会将这些内容填充到您的存储库对象数据库中。

一旦安全地保存了对象,Git就会在特殊的FETCH_HEAD文件中记录哈希ID。如果您的Git至少为1.8.4,那么此时会更新任何相应的远程跟踪分支名称:例如,它可能会更新您的origin/master

(如果您手动运行git fetch,您的Git将遵循所有正常的refspec更新规则,如the git fetch documentation中所述。它是git fetch传递给git pull的其他参数{1}}根据你的Git版本禁止其中一些。)

那么,那就是我认为你真正的第一个问题的答案:git fetch将这些对象存储在Git的对象数据库中,可以通过它们的哈希ID来检索它们。它会将哈希ID添加到.git/FETCH_HEAD(总是),并且通常还会更新refs/tags/中的部分引用标记名称和refs/remotes/中的远程跟踪分支名称。

1 除了,&#34; unshallow&#34;一个浅浅的克隆。

git pull

的其余部分

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

您可以在此处执行的两项主要操作是git mergegit rebase。理解它们的最好方法是在别处阅读它们(其他SO帖子,其他文档等)。然而,两者都是复杂的命令 - git pull有一个特殊情况,涵盖了这两个:特别是,你可以git pull成为不存在的科。在两种情况下,你有一个不存在的分支(Git也称为孤儿分支未出生的分支):

  • 在一个新的空存储库(没有提交)或
  • 运行git checkout --orphan newbranch

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

这种git pull传统上都是错误的,所以要小心:在1.8-ish之前的Git版本有时会破坏未提交的工作。我认为最好完全避免git pull:只需自己运行git fetch,然后弄清楚你想做什么。据我所知,它在现代Git中没问题 - 这些版本不会破坏你的索引和工作树 - 但我习惯于自己避免使用git pull

在任何情况下,即使您不在孤儿/未出生/不存在的分支上,尝试使用脏索引和/或工作运行git merge也不是一个好主意 - 树(&#34;未提交的作品&#34;)。 git rebase命令现在有一个自动存储选项(rebase.autoStash),因此您可以让Git自动运行git stash save以从任何此类未提交的工作中创建一些分支外提交。然后,rebase本身可以运行,之后Git可以自动应用并删除存储。

git merge命令没有此自动选项,但您当然可以手动执行此操作。

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

答案 1 :(得分:2)

  
      
  1. 但是,这仍然意味着在“git pull”之后我的工作树将与远程回购相同吗?
  2.   

不一定。您正在拉动的分支上的任何本地提交都将与上游的更改合并。使用git pull --rebase将您的本地更改置于上游提交之上。你可以在没有--rebase的情况下获得一些非常时髦的合并路径。

  
      
  1. 我发现有些情况下“git pull”不会改变我本地仓库中的任何内容或创建任何新提交?
  2.   

如果上游没有新的提交,则本地副本中的任何内容都不会改变。

  
      
  1. “git pull”仅在索引处进行更改是否有意义?
  2.   

不是我知道的。也许如果它无法与你的本地提交合并,但是你应该至少在这个过程中遇到一些错误。

  
      
  1. 如果是,我怎样才能使索引的更改继续前进到工作树?
  2.   

git pull :)或git rebase <upstream> <branchname>。这将在该分支的上游提交之上重新绑定分支<branchname>中的本地提交。

答案 2 :(得分:2)

  1. 否:
    如果您有本地提交但尚未推送,或者某些已编制索引的更改(git add ed),您仍会在上次公开提交之前进行本地更改(或与上次公开提交合并)提交);

  2. 是的:
    如果自上次git pull以来没有任何东西被推送到远程仓库,那么你已经是最新的,所以什么都不会改变;

  3. 否:
    如果您在git pull之后看到索引中的更改,则在运行git pull之前已经将文件编入索引;

  4. git已经出现以下警告:如果您的某个索引文件应由merge更新,git将不会执行合并,并打印一条消息:

    error: Your local changes to the following files would be overwritten 
    by merge:
        bb
    Please commit your changes or stash them before you merge.
    Aborting
    

    在这种情况下:您应该从索引创建提交,并运行git merge origin/current/branch(或git rebase origin/current/branch)以将远程修改与您的本地修改合并。

  5. git fetch [origin]的默认行为是读取存储在远程仓库中的所有分支,并更新存储在refs/remotes/[origin]/*下的所有本地引用。

    然后,您可以在所有标准origin/branch/name命令中使用git作为有效的树名称:

    # difference with remote "master" branch :
    $ git diff HEAD origin/master
    
    # history of remote branch "feature" alongside your local branch "feature" :
    $ git log --oneline --graphe feature origin/feature
    
    # merge changes from remote "master" :
    $ git merge origin/master
    
    # rebase your local commits on top of remote "develop" branch :
    $ git rebase origin/develop
    
    # etc ...
    

    您还有一个快捷方式,可以说&#34;远程分支链接到我的活动分支&#34; :@{u}

    $ git diff @{u}
    $ git log --oneline --graph HEAD @{u}
    $ git merge @{u}
    $ git rebase @{u}
    # etc ...
    

    什么是完全&#34; git pull&#34;做什么?

    git fetch之后,git更新名为FETCH_HEAD的特殊引用,该引用通常与活动分支的@{u}匹配;
    git pull git fetch && git merge FETH_HEAD

    我试图在上面的段落中用我自己的话解释git fetch

答案 3 :(得分:0)

获取与此时正在使用的分支相关的las changes of your remote repo

答案 4 :(得分:0)

Git pull是为了使本地分支与其远程版本保持同步,同时还更新其他分支。请阅读此处的文档:Git Pull Documentation