【问题标题】:Differences between git fetch and git fetch origin mastergit fetch 和 git fetch origin master 的区别
【发布时间】:2014-02-27 12:32:32
【问题描述】:

我是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

注意master指向不同的东西;在一种情况下FETCH_HEAD 和在另一种情况下origin/master? 它们不同吗?

【问题讨论】:

  • 有关FETCH_HEAD的信息请参见stackoverflow.com/questions/9237348/…
  • 这不是重复的,我问的是 FETCH_HEAD 和 origin/master 的区别。
  • 这正是我没有将其标记为重复的原因,而只是指出了相关信息。
  • 嗯,它没有我要查找的信息,但还是感谢您提供的链接。
  • 好吧,我不是专家,但我认为它非常完美地回答了你的问题。 “这有点像不带参数的 git fetch(或 git remote update),更新所有远程分支,然后运行 ​​git merge origin/,但在内部使用 FETCH_HEAD 来引用获取的任何单个 ref,而不是需要给事物命名。”

标签: git git-merge git-fetch


【解决方案1】:

这里是“TL;DR”版本(掩盖了很多特殊情况):git fetch总是更新FETCH_HEAD,不止一行在各种情况下。它有时会更新“远程分支”,即全名以refs/remotes/ 开头的引用。其余的主要是关于“有时”,这取决于提供给git fetch 的参数数量和 git 版本。


我有机会对此进行测试。让我们区分三种情况,所有这些情况都假定运行git fetch 没有额外的选项,如-a 甚至--all。让我们也排除git fetch 的奇怪变体,例如直接使用URL,或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 <em>remote</em>(除此之外没有更多参数)。

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

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

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

    通常还有至少一个配置条目,可能更多,内容如下:

        fetch = +refs/heads/*:refs/remotes/remote-X/*
    

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

    接下来,git fetch 询问远程它有哪些 refs(主要是分支和标签,虽然你可以获得所有 refs,但大多数人只关心分支和标签)。您可以使用git ls-remote remote-X 自己做同样的事情,它会溢出如下内容:

    676699a0e0cdfd97521f3524c763222f1c30a094        HEAD
    222c4dd303570d096f0346c3cd1dff6ea2c84f83        refs/heads/branch
    676699a0e0cdfd97521f3524c763222f1c30a094        refs/heads/master
    

    HEAD ref 的处理并不完全一致(我看到它的行为很奇怪),但通常在这里它只是被丢弃了。2 其余分支根据fetch = 参考规范。 (如果有多个fetch = refspecs,它们会根据它们全部重命名和更新。这主要用于引入refs/notes/ 或在refs/rtags/ 下创建自己的“远程标签”命名空间,例如.)

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

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

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

     * branch            HEAD       -> FETCH_HEAD
    

    在这种情况下,就好像(缺失的)fetch = 行在那里并且包含 fetch = HEAD

  3. git fetch <em>remote refspec</em>refspec 部分实际上是一个或多个参考规范,如下所述)。

    这与案例 2 类似,只是这一次,“refspecs”是在命令行上提供的,而不是来自远程的 fetch = 配置条目。但是,这里的 fetch 行为完全不同。


在这种特殊情况下,让我们暂停一下并正确描述 refspec。 (git push 也会出现 Refspecs,但与 git 一样,实现细节会泄漏出来,并且它们在那里的工作方式略有不同。) refspec 有一个可选的前导加号(+),我将在这里忽略它;3 然后是两部分,用冒号 (:) 分隔。两者通常都只是一个分支名称,但您可以(和fetch = 行一样)拼出“完整”引用名称,refs/heads/<em>branch</em> 在分支名称的情况下。

对于获取操作,左侧的名称是远程本身的名称(例如git ls-remote 所示)。右侧的名称是要在本地 git 存储库中存储/更新的名称。作为一种特殊情况,您可以在斜线后面添加一个星号 (*) 作为最后一个组件,例如 refs/heads/*,在这种情况下,左侧匹配的部分将在右侧替换。因此refs/heads/*:refs/remotes/remote-X/* 是导致refs/heads/master(在远程看到,git ls-remote)变成refs/remotes/remote-X/master(在你的本地存储库中看到,更短的形式,在-&gt; 行的右侧) git fetch 打印)。

不过,如果你不放:git fetch 就没有好地方放“那边的分支”的副本。假设它将带入遥控器的refs/heads/master(遥控器上的master 分支)。而不是更新你的refs/heads/master——如果你在分支master中有自己的提交显然会很糟糕——它只是将更新转储到FETCH_HEAD

这就是事情变得特别怪异的地方。假设您运行git fetch remote-X master branch,即至少给出一个,也许是几个,refspecs,但都没有冒号。

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

    676699a0e0cdfd97521f3524c763222f1c30a094        branch 'master' of ...
    222c4dd303570d096f0346c3cd1dff6ea2c84f83        branch 'branch' of ...
    
  • 如果您的 git 版本是 1.8.4 或更高版本,则更新将在那里进行——这部分没有改变——但也是,获取会借此机会永久地记录这些分支 在它们适当的远程分支中,如远程的fetch = 行所给出的。

    不管出于什么原因,git fetch 只为实际更新的远程分支打印出更新-&gt; 行。由于它总是记录FETCH_HEAD中的所有更新,它总是在此处打印分支名称。

    (除了需要 git 1.8.4 或更高版本之外,更新远程分支的另一个问题是那些 fetch = 行必须存在。如果它们不存在,则没有映射知道 fetch 知道重命名 @ 987654401@到refs/remotes/remote-X/*。)

换句话说,git 1.8.4 和更新版本确实会“机会性地更新”所有远程分支。老版本的git在git push上做,所以之前一直不一致。即使在 git 1.8.4 中,它仍然与git pull 不一致,我认为(尽管我使用git pull 不足以注意到:-));这应该在 git 1.9 中修复。

现在让我们回到git fetch <em>remote</em>git fetch <em>remote refspec ...</em> 之间的区别。


  • 如果您运行 git fetch <em>remote</em>,即省略所有 refspec,则 fetch 会像往常一样退回到 fetch = 行。 fetch 操作带来了来自fetch 行的所有引用。 所有这些都进入FETCH_HEAD,但这次它们被标记为“不可合并”(带有标签,我将其更改为一个空格以更好地适应网页):

    676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
    

    不是分支的 Refs,例如,refs/notes/ 带来的 refs,改为读取:

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

    同时,如果需要,远程分支 refs 会更新,并通过消息告诉您哪些已更新:

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

    再次,所有内容都被转储到FETCH_HEAD,但只有“需要更新”的参考文献被更新和打印。新分支打印“新分支”,旧分支打印其缩写的 old-and-new SHA-1,如上面的master -&gt; remote-X/master

  • 另一方面,如果您运行git fetch <em>remote refspec ...</em>,则获取指定的参考规范。这些全部像往常一样进入FETCH_HEAD6但是这次每一个都被打印出来。然后,如果您的 git 是 1.8.4 或更高版本,任何可以映射(通过合理的 fetch = 行)和需要更新的参考更新都将更新和打印

     * branch            master     -> FETCH_HEAD
     * branch            branch     -> FETCH_HEAD
       22b38d1..676699a  master     -> remote-X/master
    

    如果您的 git 版本早于 1.8.4,则在这种情况下不会发生 remote-X/master 的更新——或者更确切地说,除非您的命令行参考规范之一是 refs/heads/master:refs/remotes/remote-X/master 或 @,否则不会发生更新987654424@,或者前面加号的变体。


1这不是一个很好的错误消息。 remote-X 参数从来不应该是一个“存储库”,它应该是一个“远程”!如果 git 在这里说一些更丰富的信息可能会很好。

2git 远程协议有一个缺陷:HEAD 通常是间接引用,因为它是远程上的当前分支,所以它应该以“ref: refs/heads/master”的形式出现例如,但它作为完全解析的 SHA-1 出现。至少一个 git 命令 (git clone) 尝试通过将此 SHA-1 与每个分支头的 SHA-1 进行比较来“猜测”远程上的当前分支。例如,在上面,很明显远程是“在分支主机上”,因为HEADrefs/heads/master 具有相同的 SHA-1。但是,如果多个分支名称指向同一个提交,并且HEAD 与该提交ID 匹配,则无法判断HEAD 处于哪个分支(如果有)。遥控器也可能处于“分离 HEAD”状态,在这种情况下,它不在 any 分支上,无论 SHA-1 值如何。

2019 年编辑:此错误已在 Git 版本 1.8.4.3 中修复。只要两个 Git 版本(在您要克隆的机器上和您自己的机器上)都是 1.8.4.3 或更高版本,Git 就不必再猜测了。

3加号表示“接受强制更新”,即接受将被分支的“只是快进”4 规则拒绝的更新,或“从不更改标签”5 用于标签。

4当提交有向无环图中的旧 SHA-1 是祖先时,可以对标签进行“快进”,将其从旧的 SHA-1 更改为新的新的 SHA-1。

5“从不更改标签”规则是 git 1.8.2 中的新内容。如果你的 git 比这更旧,git 也使用标签的分支规则,允许快速转发而无需“强制更新”。

6但这次没有not-for-merge。基本上,当您提供无冒号的 refspec 时,git fetch 假定它们是“用于合并”并将它们放入 FETCH_HEAD 以便 git merge FETCH_HEAD 可以找到它们。 (我还没有测试过非分支引用会发生什么。)

【讨论】:

  • 是否必须更新到最新的 git 版本?
  • 不,只是对于较旧的版本,您需要了解各种缺点(如果有人这么看的话)和/或即将发生的变化。
猜你喜欢
  • 2013-02-08
  • 2020-07-27
  • 2018-04-11
  • 2011-02-10
  • 2016-08-06
  • 2011-02-22
  • 2012-08-11
  • 2014-03-31
相关资源
最近更新 更多