Git – 当两个遥控器具有相同的标签名称时,签出远程标签

我曾希望这会工作:

git checkout remote/tag_name 

但事实并非如此。 这样做:

 git checkout tags/tag_name 

但是我做了一些奇怪的事情,我有很多遥控器,我担心如果两个遥控器有相同的标签会发生什么。 有没有办法指定远程签出标签?

执行摘要:你想要达到什么是可能的,但首先你必须发明远程标签。

你用一系列的refspecs来做,每个遥控器一个。 其余的是关于这些是什么,他们是如何工作的,等等。


你的问题是关于检查一个“远程标签”,但Git没有远程标签,这是:

但是我做了一些奇怪的事情,我有很多遥控器,我担心如果两个遥控器有相同的标签会发生什么。 有没有办法指定远程签出标签?

显示(我认为)你的困惑的来源。

让我们来回顾一下,只是谈论Git在一般意义上的“参考”。 为了帮助实现这个想法,特定的引用forms包括本地分支名称( masterdevelfeature等),“远程分支名称”(如origin/masterstuff_from_bobs_computer/master )以及标签名称。 像Git的“藏”也使用引用,甚至HEAD是一个参考,虽然它是一个非常特殊的,通常是一个“符号”的参考。 这里的重点是,Git有很多引用forms,并且最终都以相同的方式工作:引用名称最终parsing为其中一个较大的SHA-1值676699a0e0cdfd97521f3524c763222f1c30a094或其他内容。

大多数引用 – 例外是像HEADORIG_HEADMERGE_HEAD和其他一些其他的东西 – 实际上拼写的名字以refs/开头。 这些保存在一个目录或文件夹结构, 1与子目录: refs/tags/包含您的标签, 2 refs/heads/包含所有您的分支,和refs/remotes/包含所有您的远程分支。

远程分支进一步细分为远程分支的名称: refs/remotes/origin/包含所有的远程分支,而refs/remotes/stuff_from_bobs_computer/包含所有的stuff_from_bobs_computer远程分支。 如果你有很多的遥控器,你在refs/remotes/里面有很多的子目录。

我刚刚提到你的标签都在refs/tags/ 。 遥控器的标签,所有遥控器上的所有标签怎么样? 那么,再次,git没有“远程标签”。 Git确实有“远程分支”,但实际上这些都是本地的。 它们存储在您的存储库中,在refs/remotes/标题下。

当你的Git联系一个“远程”(通常是通过git fetch remote ,而且也是为了push (以及最初的clone步骤)时,Git会问远程Git 3的问题:“你有哪些本地分支?是他们的SHA-1值? 事实上,这就是fetch工作原理:作为一个简单的描述,抓取的过程包括询问远程Git“嘿,whaddaya得到了吗? 它给你一组名称和SHA-1。 你的Git然后检查它是否有相同的SHA-1。 如果是这样,谈话就完成了; 如果不是的话,那么你的Git会说:“好的,我需要这些SHA-1的提交中的任何东西”,这实际上是另一个SHA-1s,你的Git和他们的Git会说出来哪些文件以及您需要的文件,全部由SHA-1标识。 你的Git带来了这些对象,并将新的SHA-1填充到你的refs/remotes/ ,在远程名称下,然后在它们的本地分支名称下。

如果你要求提供标签,你的Git会做得更多。 4而不是只问他们的Git关于他们的分支,你的Git也问他们的标签。 他们的Git再一次给你一个名字和SHA-1的列表。 然后你的Git把所有需要的底层对象,然后 – 这是整个问题的关键,它把它们的标记名写入你的refs/tags/

所以,当你走到远程origin并询问标签时会发生什么,它说:“我有refs/tags/pinkyrefs/tags/brain ”,这就是为你创build了本地标签的pinkybrain ,在您的参考名称空间中也命名为refs/tags/pinkyrefs/tags/brain

现在你去Bob的计算机(远程名为stuff_from_bobs_computer上面),并要求它的标签。 他进入了神经学,而不是华纳兄弟和姐妹,他的标签是refs/tags/spinal_cordrefs/tags/brain ,而第二个可能与origin关系不大。 呃哦!

事实上,这里发生的事情有点复杂,但总而言之,这只是一个糟糕的情况,你应该尽可能地避免。 有两个简单的(好的)方法来避免它。 其中一个显而易见的缺点是:只是没有得到他们的标签。 那么你将不会有任何标签冲突。 另一个是:保持所有的标签彼此分开(也可能来自你的标签)。 事实certificate,第二个并不是那么困难。 你只需要“发明”远程标签。

让我们快速浏览一下Git如何实现“远程分支”以及如何fetch --tags 。 他们都使用相同的基本机制,什么混帐调用“refspecs”。

在最简单的forms中,refspec只是看起来像两个ref名字,它们之间有一个冒号: refs/heads/master:refs/heads/master 。 事实上,你甚至可以把refs/heads/留出来,Git会把它放进去, 6有时你可以省去冒号和重复的名字。 这是你使用git push的那种东西: git push origin branch意味着使用你的refs/heads/branch来推送到origin ,并且当它到达他们的“Git”时也称它为refs/heads/branch

对于fetch ,虽然做远程分支,你得到一个refspec看起来像这样:

 +refs/heads/*:refs/remotes/origin/* 

前面的+表示“强制”, *表示明显的事情。 你的Git与他们谈话,并获得参考清单。 那些与refs/heads/*匹配的,你自己带来的(连同他们的仓库对象) – 然后它们将它们粘在的仓库中,名字叫做refs/remotes/origin/ ,现在你拥有了所有的“远程分支“从origin7

当你运行git fetch --tags ,你的git会为它使用的git fetch --tags添加+refs/tags/*:refs/tags/*8把它们的标签放在你的本地标签上。 所以你所要做的就是fetch一个refspec,如下所示:

 +refs/tags/*:refs/rtags/origin/* 

突然间你会在refs/rtags/下有一个全新的“远程标签”的名称空间(在这个例子中,只是origin )。 在这里使用+ force-flag是安全的,因为你只是更新你的标签副本:如果他们已经强制移动(或删除并重新创build)标签,你强制移动你的副本。 您可能还需要甚至需要--no-tags行为,您可以通过在命令行上指定--no-tags来获得该行为,或者,也可以看到下一段。

要知道的唯一剩下的方便的项目是git fetch从Gitconfiguration文件中为任何给定的远程获取其默认refspecs。 9如果你检查你的Gitconfiguration文件,你会看到每个远程下面的fetch =行,使用+refs/heads/*:refs/remotes/ remote-name /*string。 你可以为每个远程拥有尽可能多的fetch =行,所以你可以添加一个来取代它们的标签,但是把它们放到你的新发明的“remote tags”命名空间中。 你也可以通过在同一节中设置tagOpt = --no-tags来使--no-tags tagOpt = --no-tags这个远程的默认值。 有关详细信息,请参阅user200783的此评论 。

与所有将名称parsing为原始SHA-1的Git命令一样,然后可以通过完全引用名称对相应的SHA-1上的“分离的HEAD”模式进行git checkout

 git checkout refs/rtag/stuff_from_bobs_computer/spinal_cord 

因为Git没有内置的“远程标签”的概念,所以你必须详细说明这个长表单(详见gitrevisions )。


1实际上,它是一个真正的目录,在.git/refs 。 然而,也有一个“打包”的forms,参考.git/packed-refs 。 打包的表格是为了节省时间和精力,不经常改变的参考(或者根本就是与标签常见的)。 还有一个不断的努力来重写“后端”存储系统的参考,所以在某些时候,这很可能会改变。 Windows和Mac系统需要此更改。 Git认为分支和标签名称是区分大小写的:您可以为您的擦鞋材料分支polishPolish为您的香肠。 压缩版本区分大小写的,所以这个工作。 但存储在文件版本有时不是 ,所以它不!

2我在这里淡化轻量级和注释标签之间的区别。 带注释的标签是存储库中的实际对象,而轻量级标签是refs/tags/ space中的refs/tags/ 。 但是,一般来说,每个带注释的标签都有一个对应的轻量级标签,所以对于这个特定的用法,他们的工作原理是一样的。

3这几乎总是另一个Git回购,虽然现在有Git到Mercurial,svn等适配器。 他们有自己的假装Git回购的技巧。 而且,这种描述并不意味着是明确的:实际的操作顺序是为了传递效率而编码的,而不是为了使人类感觉到。

4我已经掩盖了一些有关简单fetchclone的特殊奇怪,即没有--tags的版本。 带有 --tags的版本很容易解释:它们会使用我在这里描述的--tags--tags所有的标签 – 至less在Git 2.10和2.11中, – --tags也会强制更新,好像+ force标志被设置。 但除非你明确地要求--no-tags ,否则一个简单的获取(和克隆)会带来一些标签。 它所做的鬼鬼祟祟的事情是查找与由于获取而进入的对象相对应的标签,并将这些标签(不强制更新)添加到(全局)标签名称空间中。 没有 – --tags你的Git不会覆盖你自己现有的标签; 用--tags ,你的Git 覆盖你自己现有的标签,至less在Git 2.10中,每个在2017年初进行的实验都是如此。

5老版本的Git在推送过程中(但不一定是获取)将“分支”规则应用于标签,允许标签更新(如果是快进),否则需要强制标志。 较新版本的git push只需要强制标签。 从 – --tags 获取 --tags没有强制标志设置,但行为就像它一样。 我还没有试用推 – – --tags 。 还有一个特别的git fetch怪异--tags vs --no-tags vs explicit --prune ,与如何--prune工作。 文档说--prune适用于任何明确的命令行refs/tags/ --tags ,但不适用于隐式的--tags 。 我还没有试验来validation这一点。

6为了让你的Git为你填写refs/heads/refs/tags/ ,你的Git必须能够弄清楚你的意思。 有些情况下,有些情况下不会。 如果你的Git没有弄明白,你会得到一个错误信息,并可以用它填充再次尝试 – 但在脚本中,你应该总是显式地填写它,以获得更多可预测的行为。 如果你只是在运行git push来推送一个已经存在的分支,你几乎总是可以让你的Git找出它。

7离开冒号和第二个名字对于git fetch来说效果并不好:它告诉你的Git不要更新你自己的引用! 这似乎毫无意义,但实际上可能是有用的,因为git fetch 总是写入特殊文件FETCH_HEAD 。 您可以将Git对象ID(SHA-1s)从特殊文件中取出并查看获取的内容。 在远程跟踪分支发明之前,这主要是从Git的早期版本开始的。

8 git fetch --tagsgit push --tags使用的git push --tags是内部预编译的,在Git 2.10版本中,由一些特殊的代码处理。 预编译的表单没有设置+标志; 但实验表明,获取的标签在Git 2.10 / 2.11中被强制更新。 我记得几年前用Git 1.x进行的实验,发现这些--tags -fetched标签没有被强制更新,所以我认为这已经改变了,但这可能只是错误的内存。 无论如何,如果你正在(重新)发明远程标签,你很可能不想使用明确的--tags

9其实这就是镜子的工作原理。 例如,用fetch = +*:*你会得到一个纯粹的获取镜像。 获取过程可以看到所有的参考。 你可以用git ls-remote自己看看。 它也是如何--single-branch工作原理:如果你在克隆过程中使用--single-branch ,你的Gitconfiguration文件将只列出在获取行中的一个分支。 要从单分支转换为全分支,只需编辑该行以包含通常的全局模式条目。

1 – 从远程获取标签:

 git fetch origin --tags 

或者,从不同的远程使用中检出标签:

 git fetch your_remote --tags 

2通过运行检查标签

 git checkout tags/<tag_name> 

更多这里: 下载一个特定的标签与Git

在我的情况下,当一个新的标签被添加到远程仓库[我正在使用Stash]时,新标签在git tag -l的结果中不可用。
但是我能够使用git ls-remote --tags查看新添加的标签。
我不得不运行以下命令来获取所有最新的标签到我的本地存储库:
git pull --tags运行git tag -l现在也显示了新添加的标签。

要结帐标签,请使用:
git checkout <tag_name>

注意:运行git状态并查找如下消息是正常的:
HEAD detached at tag_name

我脑海里有一些问题:

  • 为什么不同的遥控器有不同的代码(在同一棵树上)?
  • 为什么远程代码会影响您检出标签?

事情是这样的:

当你使用git checkout tags/fancytag签出一个标签时,它会在你当前的仓库(在你的机器上)查找适配标签。

如果你想从一个特定的远程检测一个标签,你必须首先fetch它(特定远程的树),然后检查出来。