Git - 当两个遥控器具有相同的标签名称时签出一个远程标签
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22108391/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Git - Checkout a remote tag when two remotes have the same tag name
提问by Narfanator
I had hoped this would work:
我曾希望这会奏效:
git checkout remote/tag_name
but it doesn't. This does:
但事实并非如此。这样做:
git checkout tags/tag_name
but I'm doing something weird where I have a lot of remotes, and I'm worried about what happens if two remotes have the same tag. Is there a way to specify the remote when checking out the tag?
但是我在有很多遥控器的情况下做了一些奇怪的事情,我担心如果两个遥控器具有相同的标签会发生什么。有没有办法在签出标签时指定遥控器?
回答by torek
Executive summary: what you want to achieve is possible, but first you must invent remote tags.
执行摘要:您想要实现的目标是可能的,但首先您必须发明远程标签。
You do this with a series of refspecs, one for each remote. The rest of this is about what these are, how they work, and so on.
您可以使用一系列 refspecs 来执行此操作,每个遥控器一个。剩下的就是关于这些是什么,它们是如何工作的,等等。
Your question asks about checking out a "remote tag", but Git does not haveremote tags, and this:
你的问题是问关于检查出了“远程标记”,但Git不会有远程标签,而这个:
but I'm doing something weird where I have a lot of remotes, and I'm worried about what happens if two remotes have the same tag. Is there a way to specify the remote when checking out the tag?
但是我在有很多遥控器的情况下做了一些奇怪的事情,我担心如果两个遥控器具有相同的标签会发生什么。有没有办法在签出标签时指定遥控器?
reveals (I think) the source of your confusion.
揭示(我认为)你困惑的根源。
Let's back up for a moment and just talk about what Git has in a general sense, which are "references". To help cement the idea, specific formsof references include your local branch names (master
, devel
, feature
, and so on), "remote branch names" like origin/master
and stuff_from_bobs_computer/master
, and tag names. Things like Git's "stash" also use references, and even HEAD
is a reference, though it's a very special one, and usually a "symbolic" reference. The point here is that Git has lots of forms of references, and they all really work the same way in the end: a reference name resolves, in the end, to one of those big SHA-1 values, 676699a0e0cdfd97521f3524c763222f1c30a094
or whatever.
让我们稍等片刻,简单谈谈 Git 具有的一般意义,即“引用”。为了帮助巩固这个想法,特定形式的引用包括您的本地分支名称(master
、devel
、feature
等)、“远程分支名称”(如origin/master
和stuff_from_bobs_computer/master
)和标签名称。像 Git 的“stash”这样的东西也使用了引用,甚至HEAD
是一个引用,尽管它是一个非常特殊的引用,通常是一个“符号”引用。这里的重点是 Git 有很多形式的引用,并且它们最终都以相同的方式工作:引用名称最终解析为那些大的 SHA-1 值之一,676699a0e0cdfd97521f3524c763222f1c30a094
或者其他。
Most references—the exceptions are things like HEAD
, ORIG_HEAD
, MERGE_HEAD
, and a few others along those lines—are actually spelled with names that start with refs/
. These are kept in a sort of directory- or folder-like structure,1with sub-directories: refs/tags/
contains your tags,2refs/heads/
contains all your branches, and refs/remotes/
contains all your remote branches.
大多数引用——除了像HEAD
、ORIG_HEAD
、MERGE_HEAD
和其他一些类似的东西——实际上拼写的名称都以 开头refs/
。它们保存在一种类似目录或文件夹的结构中,1 个带有子目录:refs/tags/
包含您的标签,2 个refs/heads/
包含您的所有分支,以及refs/remotes/
包含您所有的远程分支。
The remote branches are further subdivided by the name of the remote: refs/remotes/origin/
contains all the origin
remote-branches, while refs/remotes/stuff_from_bobs_computer/
contains all the stuff_from_bobs_computer
remote-branches. If you have a lot of remotes, you have a lot of sub-directories inside refs/remotes/
.
远程分支按远程名称进一步细分:refs/remotes/origin/
包含所有origin
远程分支,同时refs/remotes/stuff_from_bobs_computer/
包含所有stuff_from_bobs_computer
远程分支。如果你有很多遥控器,你里面有很多子目录refs/remotes/
。
I just mentioned that your tags are all in refs/tags/
. What about the remotes' tags, all the tags on all the various remotes? Well, again, git doesn't have "remote tags". Git does have "remote branches", but those are in fact all local. They are stored in yourrepository, under the refs/remotes/
heading.
我刚刚提到你的标签都在refs/tags/
. 遥控器的标签,各种遥控器上的所有标签呢?嗯,再说一次,git 没有“远程标签”。Git 确实有“远程分支”,但实际上它们都是本地的。它们存储在您的存储库中的refs/remotes/
标题下。
When your Git contacts a "remote"—usually through git fetch remote
, but also for push
(and the initial clone
step, for that matter), yourGit asks the remote Git3the question: "What local branches do you have? What are their SHA-1 values?" This is, in fact, how fetch
works: as a simplified description, the process of fetching consists of asking the remote Git "hey, whaddaya got?" and it gives you a set of names and SHA-1s. Your Git then checks to see if it has the same SHA-1s. If so, the conversation is done; if not, your Git then says "ok, I need whatever's in the commit(s) for these SHA-1s", which actually turns out to be another bunch of SHA-1s, and your Git and theirs talk it over to figure out which files and such you need as well, all identified by SHA-1s. Your Git brings over those objects, and stuffs the new SHA-1s into your refs/remotes/
, under the name of the remote and then under their local branch names.
当您的 Git 联系“远程”时——通常是通过,但也为(以及初始步骤,就此而言),您的Git 会向远程 Git 3提出问题:“您有哪些本地分支?它们的 SHA-1 是什么?价值观?” 这实际上是如何git fetch remote
push
clone
fetch
工作原理:作为一个简化的描述,获取的过程包括询问远程 Git “嘿,whaddaya 得到了什么?” 它为您提供了一组名称和 SHA-1。然后您的 Git 会检查它是否具有相同的 SHA-1。如果是,则对话结束;如果没有,你的 Git 然后说“好吧,我需要这些 SHA-1 的提交中的任何内容”,结果实际上是另一堆 SHA-1,你的 Git 和他们的 Git 商量了一下以找出您还需要哪些文件等,全部由 SHA-1 标识。您的 Git 将这些对象带入,并将新的 SHA-1 塞入您的 . 中refs/remotes/
,在远程名称下,然后在其本地分支名称下。
If you ask for tags with your fetch
, your Git does a bit more.4Instead of just asking their Git about their branches, your Git also asks theirs about their tags as well. Again, their Git just gives you a list of names and SHA-1s. Your Git then brings over any underlying objects needed, and then—here's the key to the whole problem—it writes their tag names into your refs/tags/
.
如果你用你的 请求标签fetch
,你的 Git 会做更多的事情。4你的 Git 不仅会询问他们的 Git 分支,还会询问他们的标签。同样,他们的 Git 只是给你一个名字和 SHA-1 的列表。然后,您的 Git 提供所需的任何底层对象,然后——这是整个问题的关键——它将它们的标签名称写入您的 .gitignore 文件中refs/tags/
。
So, what happens when you go over to remote origin
and ask it for tags, and it says "I have refs/tags/pinky
and refs/tags/brain
", is that this creates, for you, local tags pinky
and brain
, also named refs/tags/pinky
and refs/tags/brain
in your reference name-space.
因此,当您转到远程origin
并要求它提供标签时会发生什么,并且它说“我有refs/tags/pinky
和refs/tags/brain
”,这是为您创建本地标签pinky
和brain
,也在您的参考命名空间中命名refs/tags/pinky
和refs/tags/brain
。
Now you go over to Bob's computer (the remote named stuff_from_bobs_computer
above) and ask it for tags. He's into neurology, rather than Warner Brothers and Sister, and his tags are refs/tags/spinal_cord
and refs/tags/brain
, and the second one is probably not related to the one on origin
. Uh oh!
现在您转到 Bob 的计算机(stuff_from_bobs_computer
上面命名的遥控器)并要求它提供标签。他是神经病学,而不是华纳兄弟姐妹,他的标签是refs/tags/spinal_cord
和refs/tags/brain
,第二个可能与上一个无关origin
。哦哦!
Exactly what happens here gets a little bit complicated,5but in short, this is just a bad situation and you should probably avoid it if possible. There are two easy (well...) ways to avoid it. One, with obvious drawback, is: just don't get their tags. Then you won't have any tag conflicts. The other is: keep all their tags separated from each other (and maybe from yours as well). It turns out that the second one is not really that difficult. You just have to "invent" remote tags.
确切地说,这里发生的事情有点复杂,5但简而言之,这只是一个糟糕的情况,如果可能,您应该避免它。有两种简单的(嗯...)方法可以避免它。一个明显的缺点是:就是不要拿到他们的标签。那么你不会有任何标签冲突。另一个是:将所有标签彼此分开(也可能与您的标签分开)。事实证明,第二个并没有那么难。你只需要“发明”远程标签。
Let's take a quick side look at how Git actually implements "remote branches", and how fetch --tags
works. They both use the same basic mechanism, what git calls "refspecs".
让我们快速了解一下 Git 是如何实现“远程分支”的,以及它是如何fetch --tags
工作的。它们都使用相同的基本机制,git 称之为“refspecs”。
In its simplest form a refspec just looks like two ref names with a colon between them: refs/heads/master:refs/heads/master
, for instance. In fact, you can even leave out the refs/heads/
and Git will put it in for you,6and sometimes you can leave out the colon and the repeated name as well. This is the kind of thing you use with git push
: git push origin branch
means to push to origin
, using your refs/heads/branch
, and call it refs/heads/branch
when it arrives on "their" Git as well.
在最简单的形式中,refspec 只是看起来像两个 ref 名称,它们之间有一个冒号:refs/heads/master:refs/heads/master
例如。事实上,您甚至可以省略refs/heads/
,Git 会为您代入6,有时您也可以省略冒号和重复的名称。这是你使用的那种东西git push
:git push origin branch
意味着推送到origin
,使用你的refs/heads/branch
,并refs/heads/branch
在它到达“他们的”Git时调用它。
For fetch
, though, doing remote branches, you get a refspec that looks like this:
fetch
但是,对于远程分支,您会得到一个如下所示的 refspec:
+refs/heads/*:refs/remotes/origin/*
The +
at the front means "force", and the *
s do the obvious thing. Your Git talks to theirs and gets a list of refs. Those that match refs/heads/*
, yours brings over (along with their repository objects as needed)—but then it sticks them in yourrepo under names staring with refs/remotes/origin/
, and now you have all the "remote branches" from origin
.7
将+
在前面的意思是“力量”,而*
就做了明显的事情。你的 Git 与他们交谈并获取参考列表。那些比赛refs/heads/*
,(根据需要用自己的资源库对象一起)你带来了超过-但随后它坚持他们你下的名字与盯着回购refs/remotes/origin/
,现在你必须从所有的“远程分支机构” origin
。7
When you run git fetch --tags
, your git adds +refs/tags/*:refs/tags/*
to the refspecs it uses.8That brings their tags over and puts them in your local tags. So all you have to do is give fetch
a refspec that looks like:
当您运行 时git fetch --tags
,您的 git 添加+refs/tags/*:refs/tags/*
到它使用的 refspecs。8将他们的标签带过来并将它们放入您的本地标签中。所以你所要做的就是给出fetch
一个如下所示的 refspec:
+refs/tags/*:refs/rtags/origin/*
and suddenly you'll have a whole new name-space of "remote tags" under refs/rtags/
(for origin
only, in this case). It's safe to use the +
force-flag here since you are just updating your copy of their tags: if they've force-moved (or deleted and re-created) a tag, you force-move your copy. You may also want or even need --no-tags
behavior, which you can get by specifying --no-tags
on the command line, or, well, see the next paragraph.
突然之间,您将拥有一个全新的“远程标签”命名空间refs/rtags/
(origin
仅在本例中)。+
在这里使用force-flag是安全的,因为您只是在更新他们的标签副本:如果他们强制移动(或删除并重新创建)了一个标签,则您强制移动您的副本。您可能还想要甚至需要--no-tags
行为,您可以通过--no-tags
在命令行上指定来获得这些行为,或者,请参阅下一段。
The only remaining handy item to know is that git fetch
gets its default refspecs, for any given remote, from the Git config file.9If you examine your Git config file, you'll see a fetch =
line under each remote, using the +refs/heads/*:refs/remotes/remote-name/*
string. You can have as many fetch =
lines as you like per remote, so you can add one to bring over their tags, but put them in your newly-(re)invented "remote tags" name-space. You may also want to make --no-tags
the default for this remote by setting tagOpt = --no-tags
in this same section. See this comment by user200783for details.
唯一需要知道的方便的项目是git fetch
从 Git 配置文件中获取任何给定遥控器的默认 refspecs。9如果您检查 Git 配置文件,您将fetch =
在每个遥控器下看到一行,使用字符串。您可以为每个遥控器设置任意多行,因此您可以添加一个来调用它们的标签,但将它们放在您新(重新)发明的“远程标签”命名空间中。您可能还希望通过在同一部分中进行设置来为此遥控器设置默认值。有关详细信息,请参阅user200783 的此评论。+refs/heads/*:refs/remotes/remote-name/*
fetch =
--no-tags
tagOpt = --no-tags
As with all Git commands that resolve a name to a raw SHA-1, you can then git checkout
by full ref-name to get into "detached HEAD" mode on the corresponding SHA-1:
与所有将名称解析为原始 SHA-1 的 Git 命令一样,您可以git checkout
通过完整的引用名称在相应的 SHA-1 上进入“分离的 HEAD”模式:
git checkout refs/rtag/stuff_from_bobs_computer/spinal_cord
Because Git does not have the idea of "remote tags" built in, you have to spell out the long form (see gitrevisionsfor details).
因为 Git 没有内置“远程标签”的概念,所以你必须拼出长格式(详见gitrevisions)。
1In fact, it's a real directory, in .git/refs
. However, there's also a "packed" form for refs, that wind up in .git/packed-refs
. The packed form is meant to save time and effort with refs that don't change often (or at all, as is common with tags). There is also an ongoing effort to rewrite the "back end" storage system for references, so at some point a lot of this may change. This change is needed for Windows and Mac systems. Git believes that branch and tag names are case-sensitive: that you can have branch polish
for your shoeshine material, and Polish
for your sausages. The packed versions arecase-sensitive, so this works; but the stored-in-files versions sometimes aren't, so it doesn't!
1事实上,它是一个真正的目录,在.git/refs
. 但是,也有一个“打包”的 refs 形式,它以.git/packed-refs
. 打包形式是为了节省不经常更改的引用(或根本不更改,就像标签一样)的时间和精力。还有一个持续的努力来重写“后端”存储系统以供参考,因此在某些时候可能会发生很多变化。Windows 和 Mac 系统需要进行此更改。Git 认为分支和标签名称区分大小写:您可以polish
为擦鞋材料和Polish
香肠设置分支。打包的版本是大小写敏感的,所以这个作品; 但是存储在文件中的版本有时不是,所以它不是!
2I'm glossing over the difference between lightweight and annotated tags here. Annotated tags are actual objects in the repository, while lightweight tags are labels in the refs/tags/
space. However, in general, each annotated tag has one corresponding lightweight tag, so for this particular usage, they work out the same.
2我在这里掩盖了轻量级标签和带注释标签之间的区别。带注释的标签是存储库中的实际对象,而轻量级标签是refs/tags/
空间中的标签。但是,一般来说,每个带注释的标签都有一个对应的轻量级标签,因此对于这种特定用途,它们的工作方式相同。
3It's almost always another Git repo, although there are now adapters for Git to Mercurial, svn, and so on. They have their own tricks for pretending to be Git repos. Also, this description is not meant to be definitive: the actual sequence of operations is coded for transfer efficiency, rather than for making-sense-to-humans.
3它几乎总是另一个 Git 存储库,尽管现在有 Git 到 Mercurial、svn 等的适配器。他们有自己的伪装成 Git 存储库的技巧。此外,这种描述并不意味着是确定的:实际的操作序列是为传输效率编码的,而不是为了让人类理解。
4I've glossed over a bit of special weirdness about plain fetch
and clone
here, i.e., the versions without --tags
. The versions with--tags
are easy to explain: they bring over all tags using the refspecs I've described here—and, at least in Git 2.10 and 2.11, --tags
also does forced-updates, as if the +
force flag were set. But unless you explicitly call for --no-tags
, a plain fetch (and clone) brings over sometags. The sneaky thing it does is to look for tags that correspond to objects that are coming in due to the fetch, and it adds those (without forcing updates) to your (global) tags name-space. Without --tags
your Git won't overwrite your own existing tags; with --tags
, your Git willoverwrite your own existing tags, at least in Git 2.10, per actual experiments performed in early 2017.
4我已经掩盖了一些关于普通fetch
和clone
这里的特殊奇怪之处,即没有--tags
. 带有的版本--tags
很容易解释:它们使用我在此处描述的 refspecs 带入所有标签 - 并且,至少在 Git 2.10 和 2.11 中,--tags
还进行强制更新,就像+
设置了强制标志一样。但是除非您明确调用--no-tags
,否则普通获取(和克隆)会带来一些标签。它所做的偷偷摸摸的事情是寻找与由于获取而进入的对象相对应的标签,并将这些标签(不强制更新)添加到您的(全局)标签名称空间。没有--tags
你的 Git 不会覆盖你自己现有的标签;有--tags
,你的Git的意志根据 2017 年初进行的实际实验,至少在 Git 2.10 中覆盖您自己现有的标签。
5Older versions of Git applied "branch" rules to tags during push(but not necessarily fetch), allowing a tag update if it was a fast-forward, and otherwise requiring the force flag. Newer version of git push
just require the force-tag. The fetchrefspec from --tags
does not have the force flag set, yet acts as though it does. I have not experimented with push with --tags
. There's one more special git fetch
weirdness about --tags
vs --no-tags
vs explicit refspecs, having to do with how --prune
works. The documentation says that --prune
applies to any explicit command-line refs/tags/
refspecs, but not to the implicit --tags
refspec. I have not experimented to verify this, either.
5旧版本的 Git 在推送(但不一定是获取)期间对标签应用“分支”规则,如果是快进则允许标签更新,否则需要强制标志。较新版本git push
只需要 force-tag。在获取来自的Refspec--tags
没有强制标记集,但充当但它确实。我还没有尝试过推送--tags
。与vs 显式 refspecs 相比,还有一个更特别的git fetch
奇怪之处,这与工作原理有关。文档说这适用于任何显式命令行refspec,但不适用于隐式refspec。我也没有尝试过验证这一点。--tags
--no-tags
--prune
--prune
refs/tags/
--tags
6For your Git to fill in refs/heads/
or refs/tags/
for you, your Git has to be able to figure outwhich one you meant. There are some cases where it does, and some where it doesn't. If your Git fails to figure it out, you'll get an error message, and can try again with it filled in—but in scripts you should always fill it in explicitly, to get more-predictable behavior. If you are just running git push
to push an existing branch, you can almost always let your Git figure it out.
6为了让您的 Git 填写refs/heads/
或refs/tags/
为您填写,您的 Git 必须能够弄清楚您指的是哪个。在某些情况下它确实存在,而某些情况下则不存在。如果您的 Git 无法弄清楚,您将收到一条错误消息,您可以将其填充后再试一次——但在脚本中,您应该始终明确地填充它,以获得更可预测的行为。如果你只是git push
想推送一个现有的分支,你几乎总是可以让你的 Git 弄清楚。
7Leaving out the colon and the second name does not work so well for git fetch
: it tells your Git not to update your own references at all! This seems senseless, but actually canbe useful, because git fetch
alwayswrites the special file FETCH_HEAD
. You can fish the Git object IDs (SHA-1s) out of the special file and see what got fetched. This is mostly a holdover from very early versions of Git, before remote-tracking branches were invented.
7省略冒号和第二个名称对 来说效果不佳git fetch
:它告诉您的 Git 根本不要更新您自己的引用!这似乎毫无意义,但实际上可能是有用的,因为git fetch
总是写的特殊文件FETCH_HEAD
。您可以从特殊文件中提取 Git 对象 ID (SHA-1) 并查看获取的内容。这主要是在远程跟踪分支被发明之前,早期版本的 Git 的保留。
8The refspec that git fetch --tags
and git push --tags
uses is pre-compiled internally, in Git version 2.10, and handled by some special case code. The pre-compiled form does not have the +
flag set; yet experimentation shows that fetched tags are force-updated in Git 2.10/2.11. I recall experimenting years ago with Git 1.x, and finding that these --tags
-fetched tags were notforce-updated, so I think this has changed, but that may be just faulty memory. In any case, if you are (re)inventing remote tags, you most likely do notwant to use an explicit --tags
.
8git fetch --tags
和git push --tags
使用的 refspec在 Git 2.10 版本中在内部预编译,并由一些特殊情况代码处理。预编译形式没有+
设置标志;然而实验表明,在 Git 2.10/2.11 中,获取的标签是强制更新的。我记得几年前用 Git 1.x 做实验,发现这些--tags
-fetched 标签没有被强制更新,所以我认为这已经改变了,但这可能只是错误的记忆。在任何情况下,如果你是发明了远程标签(RE),你很可能就不会想用一个明确的--tags
。
9In fact, this is how mirrors work. For instance, with fetch = +*:*
you get a pure fetch mirror. The fetch process can see all refs. You can see them yourself with git ls-remote
. It's also how --single-branch
works: if you use --single-branch
during cloning, your Git config file will list only the one single branch in the fetch line. To convert from single-branch to all-branch, simply edit the line to contain the usual glob-pattern entry.
9事实上,这就是镜子的工作原理。例如,fetch = +*:*
您将获得一个纯粹的 fetch 镜像。fetch 过程可以看到所有的 refs。您可以使用git ls-remote
. 这也是如何--single-branch
工作的:如果您--single-branch
在克隆期间使用,您的 Git 配置文件将仅列出提取行中的一个分支。要将单分支转换为全分支,只需编辑该行以包含通常的 glob-pattern 条目。
回答by Russell Fair
1 - Fetch the tag from the remote with:
1 - 从遥控器获取标签:
git fetch origin --tags
Or, to checkout a tag from a different remote use:
或者,要从不同的远程使用中签出标签:
git fetch your_remote --tags
2 Check out the tag by running
2 通过运行检查标签
git checkout tags/<tag_name>
More here: Download a specific tag with Git
更多信息:使用 Git 下载特定标签
回答by hipsandy
In my case when a new tag was added to the remote repository [I'm using Stash], the new tag wasn't available in the result of git tag -l
.
But I was able to view the newly added tag using git ls-remote --tags
.
I had to run the following command to get all the latest tags to my local repository:git pull --tags
Running git tag -l
now displayed the newly added tags as well.
在我的情况下,当一个新标签被添加到远程存储库 [我正在使用 Stash] 时,新标签在git tag -l
.
但是我能够使用git ls-remote --tags
.
我必须运行以下命令才能将所有最新标签获取到我的本地存储库:git pull --tags
Running git tag -l
now 也显示了新添加的标签。
In order to checkout a tag, use:git checkout <tag_name>
要签出标签,请使用:git checkout <tag_name>
Note: It is only normal to run git status and find a message like this:HEAD detached at tag_name
注意:运行 git status 发现这样的信息是正常的:HEAD detached at tag_name
回答by Langusten Gustel
There are some questions on my mind:
我心里有几个问题:
- Why should different remotes have different code (in the same tree)?
- Why does the remote code affect you checking out tags?
- 为什么不同的遥控器应该有不同的代码(在同一棵树中)?
- 为什么远程代码会影响您签出标签?
The thing is the following:
事情是这样的:
When you check out a tag using git checkout tags/fancytag
it will look in your current repository (on your machine) for the fitting tag.
当您使用git checkout tags/fancytag
它签出标签时,它将在您当前的存储库(在您的机器上)中查找合适的标签。
If you want to checkout a tag from a specific remote you have to fetch
it (the tree of the specific remote) first and then check it out.
如果您想从特定遥控器签出标签,您必须先访问fetch
它(特定遥控器的树),然后再签出。