【问题标题】:Git: How to get the name of checked out tag when 2/more tags on same commitGit:如何在同一提交上有2个/多个标签时获取签出标签的名称
【发布时间】:2015-03-09 09:47:28
【问题描述】:
  1. 我有一个带有 2 个标签的 git 提交,如下所示:commit1-----tagA,tagB
  2. 通过git checkout tagA签出“tagA”
  3. 问题:如何获取当前结账的标签名称?我试过git describe,但它总是返回名称“tagB”,期望返回“tagA”。

    好像git describe只能返回最近的标签名,见git manual

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

还有其他方法吗?

与此问题相关的目的:

我想让文件根据标签名自动构建版本号,git describe 在 1 个带有 1 个标签的提交时工作良好,但在上述情况下没有用。

【问题讨论】:

  • 简而言之:没有简单直接的方法来确定签出的标签;签出标签时,git 会自动进入分离的 HEAD 状态。如果您真的想知道签出了哪个标签,git reflog (as torek suggests) 是您的最佳选择。
  • git nthlastcheckout 有帮助吗?
  • 谢谢,我试过git reflog,当从一个提交切换(git checkout)到另一个提交的标签时,它可以提供正确的标签名称;但是如果在同一个提交上从一个标签切换到另一个标签,标签名称将不会更新,这看起来像一个错误。 @torek
  • @chenchang 我不认为这是一个错误。 reflog 维护HEAD(或任何其他参考)的更新历史记录。当为同一个提交签出不同的标签时,签出的提交不会改变,说没有更新。没有更新,没有reflog。
  • @Zeeker 理解并同意。

标签: git git-tag git-describe


【解决方案1】:

tagAtagB 都指向一个特定的提交,所以如果你想查看或查看那个特定的提交,就 git 而言,任何一个名称都同样好(或同样坏)。

文档中的“最新标签”措辞在这里可能具有误导性(尽管这可能是您得到 tagB 作为输出的原因)。


如何得到你想要的结果

如果你想知道你给git checkout 提供了什么标签名,你可以自己保存,或者查阅HEAD 的引用日志:

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

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


“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。如果它们中的任何一个与 this SHA-1 匹配,它们就会被保存。如果有多场比赛,他们将在比赛中选出一个获胜者。

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

不过,一般来说,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 “描述”提交088c9a8,它可以被描述为以下任何一种:

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

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

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

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

v2.3.3-220-g9ab698f

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

【讨论】:

    【解决方案2】:

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

    git tag --points-at HEAD
    

    来自git tag man page

    --points-at <object>
    

    只列出给定对象的标签。

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

    【讨论】:

    • 有趣,--points-at 进入时我没有注意到(git 1.7.10 基于发行说明)。
    • 不错的选择。谢谢你。如何以编程方式选择最近标记的那个?假设我在同一个提交(HEAD)中有 1.3.2 和 1.3.9 标签。有没有一种简单的方法让它选择我想要的 1.3.9 和我添加的最新标签?
    猜你喜欢
    • 2021-09-24
    • 2018-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 2014-04-01
    • 1970-01-01
    相关资源
    最近更新 更多