(这个答案写了好久,codeWizard's answer在目的和本质上是正确的,但并不完全,所以我还是会发布这个。)
没有所谓的“远程 Git 标签”。只有“标签”。我指出这一切并不是学究气,1 但因为对于普通 Git 用户来说,这有很多困惑,而且 Git 文档也不是很有帮助2给初学者。 (不清楚是因为文档质量差导致混乱,还是文档质量差是因为这本质上有点令人困惑,或者是什么。)
有“远程分支”,更恰当地称为“远程跟踪分支”,但值得注意的是,这些实际上是本地实体。但是,没有远程标签(除非您(重新)发明它们)。只有本地标签,所以需要在本地获取标签才能使用。
特定提交名称的一般形式——Git 称之为references——是任何以refs/ 开头的字符串。以refs/heads/ 开头的字符串命名一个分支;以refs/remotes/ 开头的字符串命名远程跟踪分支;以refs/tags/ 开头的字符串命名一个标签。名称 refs/stash 是存储引用(git stash 使用的名称;注意缺少尾部斜杠)。
有一些不以refs/ 开头的不寻常的特殊情况名称:HEAD、ORIG_HEAD、MERGE_HEAD 和 CHERRY_PICK_HEAD 尤其是所有可能引用特定提交的名称(尽管HEAD 通常包含分支的名称,即包含ref: refs/heads/<em>branch</em>)。但一般来说,引用以refs/ 开头。
Git 让这件事变得混乱的一件事是,它允许您省略 refs/,通常是 refs/ 之后的单词。例如,您可以在引用本地分支或标签时省略 refs/heads/ 或 refs/tags/ — 事实上,当签出本地分支时,您必须 省略 refs/heads/!只要结果明确,或者——正如我们刚刚提到的——当你必须这样做时,你都可以这样做(对于git checkout <em>branch</em>)。
确实,引用不仅存在于您自己的存储库中,还存在于远程存储库中。但是,Git 只允许您在非常特定的时间访问远程存储库的引用:即在 fetch 和 push 操作期间。您也可以使用git ls-remote 或git remote show 来查看它们,但fetch 和push 是更有趣的联系方式。
参考规范
在fetch 和push 期间,Git 使用它调用的字符串 refspecs 在本地和远程存储库之间传输引用。因此,正是在这些时候,通过 refspecs,两个 Git 存储库可以相互同步。一旦您的姓名同步,您就可以使用与遥控器相同的姓名。不过,fetch 有一些特殊的魔力,它会影响分支名称和标签名称。
您应该将git fetch 视为指示您的 Git 调用(或者可能是文本消息)另一个 Git(“远程”)并与之对话。在此对话的早期,遥控器列出了它的所有引用:refs/heads/ 中的所有内容和refs/tags/ 中的所有内容,以及它拥有的任何其他引用。您的 Git 会扫描这些并(基于通常的 fetch refspec)重命名它们的分支。
让我们看一下名为origin的遥控器的正常参考规范:
$ git config --get-all remote.origin.fetch
+refs/heads/*:refs/remotes/origin/*
$
此 refspec 指示您的 Git 获取与 refs/heads/* 匹配的每个名称 - 即远程上的每个分支 - 并将其名称更改为 refs/remotes/origin/*,即保持匹配的部分相同,更改分支名称 (@987654360 @) 到远程跟踪分支名称(refs/remotes/,具体来说,refs/remotes/origin/)。
通过这个 refspec,origin 的分支成为远程origin 的远程跟踪分支。分支名称变为远程跟踪分支名称,其中包含远程名称,在本例中为 origin。 refspec 前面的加号 + 设置“强制”标志,即,您的远程跟踪分支将被更新以匹配远程的分支名称,无论它需要什么来匹配。 (如果没有+,分支更新仅限于“快进”更改,并且标签更新从 Git 版本 1.8.2 左右开始被忽略——在此之前应用相同的快进规则。)
标签
但是标签呢?它们没有参考规范——至少,默认情况下没有。您可以设置一个,在这种情况下,refspec 的形式取决于您;或者你可以运行git fetch --tags。使用 --tags 具有将 refs/tags/*:refs/tags/* 添加到 refspec 的效果,即,它会带来所有标签(但不会更新 your 标签,如果您已经有一个具有该名称的标签, 不管遥控器的标签是什么 编辑,2017 年 1 月:从 Git 2.10 开始,测试表明 --tags 会从遥控器的标签中强制更新您的标签,就好像 refspec 读取了 @987654372 @; 这可能与早期版本的 Git 的行为有所不同)。
注意这里没有重命名:如果远程origin 有标签xyzzy,而你没有,而你git fetch origin "refs/tags/*:refs/tags/*",你得到refs/tags/xyzzy 添加到你的存储库(指向相同的提交在遥控器上)。如果您使用+refs/tags/*:refs/tags/*,那么您的标签xyzzy(如果有的话)将被origin 中的标签替换。也就是说,refspec 上的 + force 标志意味着“用我的 Git 从他们的 Git 获得的值替换我的引用值”。
获取期间的自动标记
由于历史原因,3如果您既不使用--tags 选项也不使用--no-tags 选项,git fetch 将采取特殊措施。请记住,我们在上面说过,无论您的本地 Git 是否想要查看它们,远程都会首先向您的本地 Git 显示其所有引用。4 您的 Git 会记下它此时看到的所有标签。 然后,当它开始下载任何它需要处理它正在获取的任何提交对象时,如果其中一个提交与任何这些标签具有相同的 ID,git 将添加该标签 - 或那些标签,如果多个标签有ID - 到您的存储库。
编辑,2017 年 1 月:测试表明 Git 2.10 中的行为现在是:如果他们的 Git 提供了一个名为 T、and 的标签,那么你没有一个名为T、和与 T 关联的提交 ID 是您的 git fetch 正在检查的其分支之一的祖先,您的 Git 添加 T 到您的标签,无论有无--tags。添加--tags 会导致您的Git 获取所有他们的标签,并强制更新。
底线
您可能必须使用git fetch --tags 来获取他们的标签。如果他们的标签名称与您现有的标签名称冲突,您可能(取决于 Git 版本)甚至必须删除(或重命名)您的一些标签,然后运行 git fetch --tags,以获取他们的标签.由于标签(与远程分支不同)没有自动重命名功能,因此您的标签名称必须与其标签名称匹配,这就是您可能会遇到冲突问题的原因。
在大多数正常情况下,一个简单的git fetch 将完成这项工作,带来他们的提交和他们的匹配标签,因为他们——无论他们是谁——都会在当时标记提交他们发布这些提交,您将跟上他们的标签。如果您不制作自己的标签,也不混合它们的存储库和其他存储库(通过多个遥控器),您也不会有任何标签名称冲突,因此您不必大惊小怪地删除或重命名标签以获取他们的标签。
当您需要限定名称时
我在上面提到过,refs/ 几乎总是可以省略,refs/heads/ 和 refs/tags/ 等大部分时间都可以省略。但是当不能你呢?
完整(或几乎完整)的答案在the gitrevisions documentation 中。 Git 将使用链接中给出的六步序列将名称解析为提交 ID。奇怪的是,标签会覆盖分支:如果有标签xyzzy 和分支xyzzy,并且它们指向不同的提交,那么:
git rev-parse xyzzy
会给你标签指向的ID。然而——这正是gitrevisions 所缺少的——git checkout 更喜欢分支名称,所以git checkout xyzzy 会将你放在分支上,而忽略标签。
如果有歧义,您几乎总是可以使用其全名refs/heads/xyzzy 或refs/tags/xyzzy 拼出引用名称。 (请注意,这确实适用于git checkout,但可能以一种意想不到的方式:git checkout refs/heads/xyzzy 导致分离头检出而不是分支检出。这就是为什么你只需要注意@ 987654404@ 将首先使用短名称作为分支名称:即使标签 xyzzy 存在,这也是您签出分支 xyzzy 的方式。如果要签出标签,可以使用 refs/tags/xyzzy。)
因为(如gitrevisions 注释)Git 会尝试refs/<em>name</em>,您也可以简单地写tags/xyzzy 来识别标记为xyzzy 的提交。 (如果有人设法将名为xyzzy 的有效引用写入$GIT_DIR,则这将解析为$GIT_DIR/xyzzy。但通常只有各种*HEAD 名称应该在$GIT_DIR 中。)
1好吧,好吧,“不是只是学究”。 :-)
2有些人会说“非常没有帮助”,实际上我倾向于同意。
3基本上,git fetch 以及远程和 refspecs 的整个概念,是在 Git 1.5 左右出现的较晚添加到 Git 的。在此之前,只有一些特殊的特殊情况,标签获取就是其中之一,所以它通过特殊代码被继承了。
4如果有帮助,请将远程 Git 想象成 flasher,这是俚语的意思。