4
  1. 我有一个带有 2 个标签的 git commit,如下所示:commit1-----tagA,tagB
  2. 结帐“tagA”git checkout tagA
  3. 问题:如何获取当前结账的标签名称?我试过git describe了,但它总是返回名称“tagB”,期望返回“tagA”。

    似乎git describe只能返回最近的标签名,见git 手册

    该命令查找可从提交中访问的最新标记。如果标签指向提交,则只显示标签。否则,它会在标记名称的后缀加上标记对象顶部的附加提交数量和最近提交的缩写对象名称。

还有其他方法吗?

目的与此问题相关:

我想让文件从标签名称自动构建版本号,git describe当 1 个带有 1 个标签的提交时效果很好,但在上述情况下它没有用。

4

2 回答 2

3

您至少可以获取给定提交的所有标签:

git tag --points-at HEAD

git tag手册页

--points-at <object>

仅列出给定对象的标签。

从那里,你可以提取你想要的。

于 2015-03-09T10:00:44.407 回答
3

两者tagA和都tagB指向一个特定的提交,因此如果您想查看或查看该特定提交,就 git 而言,任何一个名称都同样好(或同样坏)。

文档的措辞“最新标签”在这里可能会产生误导(尽管这可能是您获得tagB输出的原因)。


如何得到你想要的结果

如果你想知道你给的标签名git checkout,你可以自己保存,或者查阅 reflog HEAD

8004647 HEAD@{0}: checkout: moving from master to v2.3.1

reflog 方法的优点是它已经实现了;但是 reflogs 只保留一段时间(可配置,默认 90 天用于可访问的引用),然后过期以防止 reflogs 永远增长。


“git describe”如何得出你不想要的答案

“最近”意味着如果标签具有相关的日期和时间戳——带注释的标签有,轻量级标签没有——那么那些时间较晚的标签被认为是“更好的”,如下所述。

请注意,任何时候,任何 git 命令都可以轻松查找所有外部引用并将其转换为 SHA-1。要查看它是如何工作的,只需运行git for-each-ref(您可能希望将其通过管道传输到类似的寻呼机less)。输出看起来像这样:

c2e8e4b9da4d007b15faa2e3d407b2fd279f0572 commit refs/heads/maint
9ab698f4000a736864c41f57fbae1e021ac27799 commit refs/heads/master
[snip]
74d2a8cf12bf102a8cedaf66736503bb3fe88dfb tag    refs/tags/v2.2.0
[more snippage]

这些是分支和标签——在这个 git 存储库(对于 git 本身)中,所有标签都被注释了——以及它们对应的 SHA-1。如果有一个活动的存储,它也会显示出来(在 下refs/stash)。

在任何情况下,假设git describe此时具有其中一个提交 ID(一个 SHA-1),并且describe还找到了两个或多个解析为该 ID 的名称。这些可能只是带注释的标签名称,这是您在没有选项的情况下得到的,或者它们可能是 允许的另一个名称(如分支名称)--all,例如;但重要的是假设有两个或多个名称,都指向同一个提交。

describe命令可以尝试记住所有这些名称,但它没有。取而代之的是,它通过一次两次的锦标赛来运行名称,以查看哪个“赢得比赛”:

  • 如果两个名称都是带注释的标签,请使用两个名称上的日期戳来选择要保留的标签,以及要丢弃的标签。这里的想法似乎是,如果给同一个提交两个或多个带注释的标签,那么稍后创建的那个可能“更好”并且git describe应该使用。
  • 如果一个名字是带注释的标签,另一个不是,保留带注释的标签;扔掉另一个名字。换句话说,任何带注释的标签都比任何轻量级标签都要好。
  • 如果两个名称都不是带注释的标签,但一个是(轻量级)标签而另一个不是,则保留该标签;扔掉非标签。换句话说,任何标签都比其他任何标签都好。
  • 否则(两个名称都不是任何类型的标签),保留“最早遇到”的名称(这取决于 git 的内部名称遍历机制,因此不一定是可预测的)。

一旦此提交的所有名称相互竞争以选择“获胜名称”,“获胜名称”将与提交 SHA-1 一起保存。


现在,我们首先如何获得该提交 ID?答案是git describe从你的论点开始:

$ git describe       # no args, means ...
$ git describe HEAD  # use HEAD to get the SHA-1

您的参数(或HEAD)使用git rev-parse(好吧,它的 C 代码等效项)转换为适当的原始 SHA-1:

$ git rev-parse HEAD
9ab698f4000a736864c41f57fbae1e021ac27799

然后,git describe调用git for-each-ref(或其等效的 C 代码)将您允许它使用的所有名称(默认情况下,所有带注释的标签)转换为 SHA-1 ID。如果其中任何一个与SHA-1 匹配,则它们将被保存。如果有多场比赛,他们将通过比赛来挑选一个获胜者。

对于您的特定情况,这部分成功:两者tagA都是tagB完全匹配的,因此在这两者之间选择了一个获胜标签后,整个事情就停止了。在您的情况下,那是您不想要的标签。

但是,总的来说,git describe通常必须继续前进。例如,考虑来自git项目本身的以下提交:

088c9a8 strbuf.h: format asciidoc code blocks as 4-space indent
aa07cac strbuf.h: drop asciidoc list formatting from API docs
6afbbdd strbuf.h: unify documentation comments beginnings
bdfdaa4 strbuf.h: integrate api-strbuf.txt documentation
eae6953 tests: correct misuses of POSIXPERM
1767c51 t/lib-httpd: switch SANITY check for NOT_ROOT
b4a56a3 "log --pretty" documentation: do not forget "tformat:"

现在假设我们让一个标签X指向提交b4a56a3,一个标签Y指向提交eae6953,一个标签Z指向提交aa07cac。如果我们然后让 git “描述” commit 088c9a8,它可以被描述为以下任何一种:

  • 提交后六步发生的提交X,或
  • 提交后四个步骤发生的提交Y,或
  • 提交后一步发生的提交Z

的输出git describe将使用 tag Z,因为从“tag Z”(提交aa07cac)到有问题的提交(088c9a8)需要更少的步骤。这实际上是如何发生的非常复杂(也许是不必要的,尽管这是一个价值判断:-))复杂。

这里要做git describe的是遍历(部分)提交图(--first-parent如果指定,则应用)以找到确实有名称的“附近”提交。搜索顺序有点难以描述:它部分是基于日期的(a lagit log的按日期排序的提交列表),但如果遇到带注释的标签,它会提前停止。否则,它会累积“候选名称”列表(使用上述竞赛方法),当它累积足够多(默认 10 个)候选名称时停止,然后对列表进行排序。排序是基于“图距离”的,所以假设X,YZ都是轻量级标签——如果有的话是带注释的标签,它现在已经被选中并且搜索停止了——<code>Z 在这里获胜。

最后,找到一个指向合适提交的名称,该提交实际上不是给定的参数提交,git describe打印名称、距离的步数(“深度”)以及g实际 SHA-1 的一部分:

v2.3.3-220-g9ab698f

(尽管深度和g可以被抑制,如果根本找不到名字或者如果你只要求完全匹配,那么整个事情可能会失败,等等)。

于 2015-03-09T10:01:31.387 回答