git fetch和git fetch origin master之间的差异

时间:2014-02-03 20:37:51

标签: git git-merge git-fetch

我是doing a fetch/merge,想知道做

之间是否有任何区别
git fetch

git fetch origin master

我在GitHub上的remote repository没有任何其他分支和原点。

当我这样做时:

git fetch origin master
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:XXXXXXXXXXXXXXX
 * branch            master     -> FETCH_HEAD

但只是:

git fetch
From github.com:XXXXXXXXXXXXXXX
   531d466..aaf6df0  master     -> origin/master

注意主人指向不同的东西;在一个案例中FETCH_HEAD,在其他情况下,origin/master? 他们不一样吗?

1 个答案:

答案 0 :(得分:6)

这里是" TL; DR"版本(隐藏在批次的特殊情况下):git fetch 总是更新FETCH_HEAD,在各种情况下都有多行。它有时更新"远程分支",这些是全名以refs/remotes/开头的引用。其余的主要是关于"有时",这取决于git fetch和git版本的参数数量。


我有机会测试这个。让我们区分三种情况,所有这些情况都假定在没有git fetch甚至-a等额外选项的情况下运行--all。我们还排除git fetch的怪异变体,例如直接使用网址,insteadOf条目或.git/remotes.git/branches中列出的文件。 (我承认我只是在猜测,但我认为这些都是[remote "name"]条目进入git配置文件前的剩余时间。编辑,2019:结果证明是正确的。)

  1. git fetch,没有其他参数。

    Git确定您当前的分支(以通常的方式,通过阅读HEAD,但您当然可以看到git branchgit status的内容。然后,它会查找该分支的配置条目,并将其命名为remote。例如,假设您在分支dummy.git/config上(以及其他条目):

    [branch "dummy"]
        remote = remote-X
    

    在这种情况下,git fetch相当于git fetch remote-X。在那之后,这相当于案例2,即:

  2. git fetch remote(除此之外不再有其他论据)。

    这次Git不看你当前的分支。要使用的遥控器是命令行上给出的遥控器。它 查找给定远程的配置部分。我们假设您正在使用remote-X:在这种情况下,它会查找:

    [remote "remote-X"]
        url = ...
    

    如果该部分不存在,或者没有url =条目,则会收到错误:fatal: 'remote-X' does not appear to be a git repository 1 否则会提供URL,{{1将尝试连接到那里。假设它可以连接......

    通常还有至少一个配置条目,可能更多,阅读:

    git fetch

    (遥控器的名称在这里是硬编码的)。假设有......

    接下来, fetch = +refs/heads/*:refs/remotes/remote-X/* 向遥控器询问它有什么内容(主要是分支和标签,尽管你可以获得所有引用,但大多数人只关心分支和标签)。你可以使用git fetch自己做同样的事情,这会溢出这样的东西:

    git ls-remote remote-X

    676699a0e0cdfd97521f3524c763222f1c30a094 HEAD 222c4dd303570d096f0346c3cd1dff6ea2c84f83 refs/heads/branch 676699a0e0cdfd97521f3524c763222f1c30a094 refs/heads/master ref的处理并不完全一致(我看到它表现得很奇怪),但通常在这里它会被丢弃。 2 其余的分支被重命名,根据{{​​1}} refspec更新。 (如果有多个HEAD refspec,则会根据所有这些重新命名和更新。这主要用于引入fetch =或制作自己的"远程标记&#34 ;例如,fetch =下的名称空间。)

    在这种情况下,fetch将带来两个分支refs/notes/refs/rtags/所需的任何对象,并更新(本地)"远程分支"根据需要,名称branchmaster。对于每个更新的版本,refs/remotes/remote-X/branch会打印一行如下:

    refs/remotes/remote-X/master

    如果缺少fetch,您会得到完全不同的内容。输出将显示为:

       22b38d1..676699a  master     -> remote-X/master
    

    在这种情况下,它就像(缺少的)fetch =行一样,包含 * branch HEAD -> FETCH_HEAD

  3. fetch = fetch = HEAD 部分是一个或多个refspec,实际上,如下所述。)

    这类似于案例2,只是这一次," refspecs"在命令行上提供,而不是从远程的git fetch remote refspec配置条目提供。但是,这里的获取行为非常不同。


  4. 在这种特殊情况下,让我们暂停片刻并正确描述一个refspec。 (对于refspec也会出现Refspec,但与git一样,实现细节会泄露出来,并且它们的工作方式略有不同。)refspec有一个可选的前导加号(fetch =)符号,我可以使用它。 ll忽略这里; 3 然后是两个部分,用冒号(git push)分隔。两者通常只是一个分支名称,但你可以(和+行)拼出" full"在分支名称的情况下引用名称:

    对于提取操作,左侧的名称是遥控器本身的名称(例如,如fetch =所示)。右侧的名称是要在本地git存储库中存储/更新的名称。作为一种特殊情况,您可以在斜杠之后使用星号(refs/heads/branch)作为最后一个组件,例如git ls-remote,在这种情况下,左侧匹配的部分将替换为右侧。因此*导致refs/heads/*(在远程上看到,refs/heads/*:refs/remotes/remote-X/*)成为refs/heads/master的原因(如本地存储库中所示,并且以较短的形式显示在git ls-remote上) refs/remotes/remote-X/master->的右侧打印)。

    如果你没有放入git fetch:没有好的地方可以把#34;那个分支的副本放在那里"。让我们说它将带来遥控器git fetch(遥控器上的refs/heads/master分支)。而不是更新您的 master - 如果您在分支refs/heads/master中拥有自己的提交,那么这显然会很糟糕 - 它只是将更新转储到master

    这里的事情特别棘手。我们假设您运行FETCH_HEAD,即至少提供一个,甚至几个refspecs,但都没有冒号。

    • 如果您的git版本低于1.8.4,则更新进入git fetch remote-X master branch。如果您提供了两个无冒号的refspec,FETCH_HEAD现在包含两个行:

      FETCH_HEAD
    • 如果你的git版本是1.8.4或更新版本,那么更新就会在那里 - 这部分没有改变 - 但,fetch有机会永久记录这些分支 在其正确的远程分支中,由远程的676699a0e0cdfd97521f3524c763222f1c30a094 branch 'master' of ... 222c4dd303570d096f0346c3cd1dff6ea2c84f83 branch 'branch' of ... 行给出。

      无论出于何种原因,fetch =仅为实际更新的远程分支打印出更新git fetch行。由于总是记录->中的所有更新,因此始终会在此处打印分支名称。

      (另一个问题,除了需要git 1.8.4或更新版本,更新远程分支是必须存在那些FETCH_HEAD行。如果他们没有,那么就没有映射fetch知道将fetch =重命名为refs/heads/*。)

    换句话说,git 1.8.4和更新的确实会机会性地更新"所有的远程分支机构。较旧版本的git在refs/remotes/remote-X/*上执行,因此它之前一直不一致。即使在git 1.8.4中它仍然与git push不一致,我认为(尽管我不会git pull使用足够的注意:-));应该在git 1.9中修复。

    现在让我们回到git pullgit fetch remote之间的差异。


    • 如果您运行git fetch remote refspec ...,即省略所有refspecs,则提取会像往常一样回退到git fetch remote行。获取操作会带来fetch =行的所有引用。 所有这些都会进入fetch,但这次他们会标记为" not-for-merge" (使用标签,我更改为一个空格以更好地适应网页):

      FETCH_HEAD

      不是分支的引用,例如676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ... 引用的引用,而是读取:

      refs/notes/

      同时,如有必要,会更新远程分支引用,并显示消息,告知您哪些已更新:

      f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
      

      同样,所有都会被转储到 22b38d1..676699a master -> remote-X/master ,但只会引用"需要更新"更新和打印。新的分支机构获得了新的分支机构"印刷版和旧版SHA-1都印有缩写的新旧SHA-1,与上面的FETCH_HEAD一样。

    • 另一方面,如果您运行master -> remote-X/master,则提取只会带来 指定的refspecs。这些 all 像往常一样进入git fetch remote refspec ... 6 ,但这一次都会打印出来。然后,如果你的git是1.8.4或更高版本,任何可以映射的参考更新(通过合理的FETCH_HEAD行)并且需要更新更新并打印:

      fetch =

      如果您的git版本低于1.8.4,则在这种情况下不会发生 * branch master -> FETCH_HEAD * branch branch -> FETCH_HEAD 22b38d1..676699a master -> remote-X/master 的更新 - 或者更确切地说,除非您的某个命令行refspec是{{1},否则不会发生更新},或remote-X/master,或前面带加号的那些变体。


    1 这不是一个很好的错误消息。 refs/heads/master:refs/remotes/remote-X/master参数从来不应该是"存储库"它应该是一个"远程"!如果git在这里说了更多信息,那可能会很好。

    2 git远程协议存在缺陷:HEAD通常是间接引用,因为它是远程协议上的当前分支,所以它应该以&#结束34; ref:refs / heads / master"例如,它取而代之的是完全解析的SHA-1。至少有一个git命令(refs/heads/*:refs/remotes/remote-X/*)尝试"猜测"通过将此SHA-1与每个分支头的SHA-1进行比较,可以获得远程控制器上的当前分支。例如,在上面,很清楚遥控器是分支机构",因为remote-Xgit clone具有相同的SHA-1。但是,如果多个分支名称指向同一个提交,并且HEAD匹配该提交ID,则无法确定哪个分支(如果有)refs/heads/master处于打开状态。遥控器可以在"分离的HEAD"状态也是如此,在这种情况下它不在任何分支上,无论SHA-1值如何。

    编辑,2019:这个错误在Git版本1.8.4.3中得到修复。只要你在自己机器上克隆的机器上的两个Git版本都是1.8.4.3或更新版本,Git就不用再猜测了。

    3 加号意味着"接受强制更新",即采取将被"拒绝的更新除了快进" 分支的4 规则,或者"永远不会为标签更改标签" 5

    4 A"快进"对于标签,当提交指向非循环图中的旧SHA-1是新SHA-1的祖先时,可以将其从旧的SHA-1更改为新的SHA-1。

    5 "永远不会改变标签"规则在git 1.8.2中是新的。如果你的git比那个旧,那么git也会使用标签的分支规则,允许快速转发而不会强制更新"。

    6 但这次没有HEAD。基本上,当你提供无冒号的refspecs时,HEAD假设他们已经"对于合并"并将它们放入not-for-merge,以便git fetch可以找到它们。 (我还没有测试非分支引用会发生什么。)