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

Nar*_*tor 47 git git-remote git-tag

我曾希望这会奏效:

git checkout remote/tag_name
Run Code Online (Sandbox Code Playgroud)

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

git checkout tags/tag_name
Run Code Online (Sandbox Code Playgroud)

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

小智 82

1 - 使用以下命令从遥控器获取标签:

git fetch origin --tags 
Run Code Online (Sandbox Code Playgroud)

或者,从不同的远程用户签出标签:

git fetch your_remote --tags
Run Code Online (Sandbox Code Playgroud)

2通过运行检查标签

git checkout tags/<tag_name>
Run Code Online (Sandbox Code Playgroud)

更多信息:使用Git下载特定标签


tor*_*rek 77

执行摘要:您希望实现的目标是什么,但首先您必须发明远程标签.

您可以使用一系列refspec执行此操作,每个远程控制一个.剩下的就是这些是什么,它们如何工作,等等.


你的问题是问关于检查出了"远程标记",但Git不会有远程标签,而这个:

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

揭示(我认为)你困惑的根源.

让我们回顾一下,然后谈谈Git在一般意义上的含义,即"引用".为了帮助巩固这个想法,具体的形式引用的包括当地分行名称(master,devel,feature,等),"远程分支的名字"像origin/master和stuff_from_bobs_computer/master,和标签名称.像Git的"藏匿"之类的东西也使用引用,甚至HEAD是引用,虽然它是一个非常特殊的引用,通常是一个"符号"引用.这里的要点是Git有很多形式的引用,它们最终都以相同的方式工作:引用名最终解析为其中一个大的SHA-1值,676699a0e0cdfd97521f3524c763222f1c30a094或者其他什么.

大多数引用 - 例外是像HEAD,ORIG_HEAD和MERGE_HEAD,以及沿着这些行的其他一些 - 实际上拼写的名称以refs/.它们保存在一种类似目录或文件夹的结构中,1带有子目录:refs/tags/包含您的标签,2 refs/heads/包含所有分支,并refs/remotes/包含所有远程分支.

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

我刚刚提到你的标签都在refs/tags/.遥控器的标签,所有各种遥控器上的所有标签怎么样?好吧,再次,git没有"远程标签".Git确实有"远程分支",但实际上这些都是本地的.它们存储在你的资料库,下refs/remotes/标题.

当你的Git联系一个"远程" - 通常,但也为(和最初的步骤),你的 Git问远程Git 3的问题:"你有什么本地分支?他们的SHA-1是什么?值?" 事实上,这是多么有效:作为一个简化的描述,获取的过程包括询问远程Git"嘿,whaddaya得到了吗?" 它为您提供了一组名称和SHA-1.然后你的Git会检查它是否有相同的SHA-1.如果是这样,谈话就完成了; 如果没有,你的Git然后说"好吧,我需要提供这些SHA-1的提交中的任何东西",实际上它们是另一堆SHA-1,而你的Git和他们的Git谈论它您需要哪些文件等,所有文件都由SHA-1标识.你的Git会带来这些对象,并将新的SHA-1填入您的远程名称下,然后填入其本地分支名称.git fetch remotepushclonefetchrefs/remotes/

如果你要求用你的标签fetch,你的Git会做得更多.4 Git也不仅向Git询问他们的分支,而是向他们询问他们的标签.同样,他们的Git只给你一个名字和SHA-1的列表.然后你的Git会带来所需的任何底层对象,然后 - 这里是整个问题的关键 - 它将你的标签名称写入你的refs/tags/.

所以,当你去到偏远发生了什么origin,并要求它的标签,并说"我有refs/tags/pinky和refs/tags/brain",是这个造成的,对你来说,局部变量pinky和brain,也被命名refs/tags/pinky并refs/tags/brain在您的参考名称空间.

现在你转到Bob的计算机(stuff_from_bobs_computer上面命名的遥控器)并询问它的标签.他进入了神经学,而不是华纳兄弟和姐妹,他的标签是refs/tags/spinal_cord和refs/tags/brain,而第二个可能与那个没关系origin.哦哦!

究竟这里发生的事情有点复杂,5但简而言之,这只是一个糟糕的情况,你应该尽可能避免它.有两种简单(好......)的方法可以避免它.一个有明显缺点的是:只是不要得到他们的标签.那么你就不会有任何标签冲突.另一个是:保持所有标签彼此分开(也可能与你的标签分开).事实证明,第二个并不是那么困难.你只需要"发明"远程标签.

让我们快速侧面看看Git如何实现"远程分支",以及它是如何fetch --tags工作的.它们都使用相同的基本机制,git称之为"refspecs".

在最简单的形式中,refspec看起来就像两个引用名称,它们之间有一个冒号:refs/heads/master:refs/heads/master例如.事实上,你甚至可以省略refs/heads/,Git会把它放进去,6有时候你可以省略冒号和重复的名字.这是你使用的东西git push:git push origin branch用来推送origin,使用你的方式refs/heads/branch,并refs/heads/branch在它到达"他们的"Git时调用它.

fetch但是,对于远程分支,您会得到一个如下所示的refspec:

+refs/heads/*:refs/remotes/origin/*
Run Code Online (Sandbox Code Playgroud)

将+在前面的意思是"力量",而*就做了明显的事情.你的Git与他们谈话并获得一份参考列表.那些匹配refs/heads/*,你的东西(以及他们的存储库对象,根据需要) - 但然后它在你的名称盯着你的回购中refs/remotes/origin/,现在你有所有的"远程分支" origin.7

当你运行时git fetch --tags,你的git会添加+refs/tags/*:refs/tags/*它使用的refspecs.8 它们会将标记放在您的本地标记中.所以你要做的就是给出fetch一个如下所示的refspec:

+refs/tags/*:refs/rtags/origin/*
Run Code Online (Sandbox Code Playgroud)

突然之间你会有一个全新的"远程标签"名称空间refs/rtags/(origin仅在这种情况下).+在这里使用force-flag 是安全的,因为你只是更新他们的标签副本:如果他们强制移动(或删除并重新创建)标签,你强制移动你的副本.您可能还需要甚至需要--no-tags行为,您可以通过--no-tags在命令行上指定,或者,看看下一段.

唯一剩下的便利项目是git fetch从Git配置文件获取任何给定远程的默认refspecs.9 如果你检查你的Git配置文件,你会看到fetch =每个遥控器下面的一行,使用字符串.每个遥控器可以拥有任意数量的线路,因此您可以添加一个来覆盖它们的标签,但将它们放入新的(重新)发明的"远程标签"名称空间中.您可能还想通过在同一部分中进行设置来设置此遥控器的默认值.有关详细信息,请参阅user200783的此评论.+refs/heads/*:refs/remotes/remote-name/*fetch =--no-tagstagOpt = --no-tags

与将名称解析为原始SHA-1的所有Git命令一样,您可以git checkout通过完全引用名称在相应的SHA-1上进入"分离的HEAD"模式:

git checkout refs/rtag/stuff_from_bobs_computer/spinal_cord
Run Code Online (Sandbox Code Playgroud)

因为Git没有内置"远程标签"的想法,所以你必须拼出长形式(详见gitrevisions).


1实际上,它是一个真正的目录,在.git/refs.然而,还有一个"包装"形式的参考,最终结束.git/packed-refs.打包表单旨在通过不经常更改的引用(或者根本不像标记一样)来节省时间和精力.还有一个不断努力重写"后端"存储系统以供参考,所以在某些时候,很多这可能会改变.Windows和Mac系统需要进行此更改.Git认为分支和标签名称区分大小写:您可以polish为您的擦鞋材料和Polish香肠分支.打包的版本是大小写敏感的,所以此工程; 但存储文件版本有时不是,所以它没有!

2我在这里浏览轻量级和带注释标签之间的区别.带注释的标签是存储库中的实际对象,而轻量级标签是refs/tags/空间中的标签.但是,通常,每个带注释的标记都有一个对应的轻量级标记,因此对于这种特定用法,它们的效果相同.

3它几乎总是另一个Git repo,虽然现在有适用于Git的Mercurial,svn等适配器.他们有自己的技巧假装成Git回购.此外,该描述并不意味着确定性:实际的操作顺序被编码用于传递效率,而不是用于对人类产生感觉.

4我已经掩盖了一些关于普通fetch和clone这里的特殊怪异,即没有版本--tags.该版本有 --tags很容易解释:他们带来了用我所描述的refspecs所有标签在这里和,至少在Git的2.10和2.11,--tags也不会强制更新,如果+强制标志被设定.但除非你明确要求--no-tags,否则普通的提取(和克隆)会带来一些标签.它做的偷偷摸摸的事情是寻找与由于获取而进入的对象相对应的标签,并将它们(不强制更新)添加到您的(全局)标签名称空间.没有--tags你的Git就不会覆盖你自己现有的标签; 有了--tags,你的Git 将覆盖你自己现有的标签,至少在Git 2.10中,根据2017年初进行的实际实验.

5旧版本的Git在推送期间(但不一定是提取)将"分支"规则应用于标记,如果它是快进的话,允许标记更新,否则需要强制标记.较新版本git push只需要force-tag.在获取来自的Refspec --tags没有强制标记集,但充当但它确实.我还没有试过推动--tags.git fetch关于--tagsvs --no-tags与明确的refspecs 还有一个特别的怪异,与--prune工作方式有关.文档说明--prune适用于任何显式命令行refs/tags/refspecs,但不适用于隐式--tagsrefspec.我也没有尝试验证这一点.

6为了让你的Git填写refs/heads/或refs/tags/为你填写,你的Git必须能够找出你的意思.在某些情况下它会发生,而某些情况则不然.如果您的Git无法弄清楚,您将收到一条错误消息,并且可以再次尝试填写 - 但在脚本中您应该始终明确地填写它,以获得更可预测的行为.如果你只是git push想要推动现有分支,你几乎总能让你的Git搞清楚.

7退出冒号和第二个名称不能很好地工作git fetch:它告诉你的Git不要更新你自己的引用!这似乎毫无意义,但实际上可能很有用,因为git fetch 总是写特殊文件FETCH_HEAD.您可以从特殊文件中提取Git对象ID(SHA-1)并查看获取的内容.在远程跟踪分支机构发明之前,这主要是Git的早期版本的延续.

8是refspec的格式git fetch --tags和git push --tags采用的是预编译的内部,在Git版本2.10,并通过一些特殊情况下的代码来处理.预编译的表格没有+设置标志; 然而实验表明,在Git 2.10/2.11中强制更新了获取的标签.我记得几年前用Git 1.x进行了实验,并发现这些--tags获取的标签没有强制更新,所以我认为这已经改变了,但这可能只是错误的记忆.在任何情况下,如果你是(重新)发明了远程标签,你最有可能做不希望使用一个明确的--tags.

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

  • git fetch remote --tags + refs/tags/*:refs/rtags/remote/* (3认同)
  • git checkout refs/rtag/remote/tag_name (3认同)
  • 我知道我不应该对这类事情使用评论而且从不这样做,但我正在做例外.这是一个很好的答案,我学到了更多关于git整体然后任何doc或blob帖子.非常感谢你花时间写这个@torek. (3认同)

hip*_*ndy 9

在我的情况下,当一个新标签被添加到远程存储库[我正在使用Stash]时,新标签在结果​​中不可用git tag -l.
但我能够使用查看新添加的标签git ls-remote --tags.
我必须运行以下命令才能将所有最新的标签提供给我的本地存储库:
git pull --tags Running git tag -l现在也显示了新添加的标签.

要签出代码,请使用:
git checkout <tag_name>

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