我如何过滤 git 登录多个标签

How i can filter git log on multiple tags

在我提交到 master 之后,我 运行 几个 CI 标记了他们成功完成的提交。假设我有两个 CI - 单元测试和集成测试都在提交时创建标签。说 unittest-signoff/{number}integrationtest-signoff/{number},其中 {number} 是自动递增的数字以确保唯一性。

如果我执行 git log -n1 --tags="unittest-signoff" 这将给我最新的提交,该提交已经从单元测试中签署。 git log -n1 --tags="integrationtest-signoff"

相同

我的问题是,什么命令会给我同时具有两个标签的最新提交。

我原以为 git 会为此准备一些东西,但我没有找到。尽管如此,我仍然相信我以某种方式错过了它,也许使用了一些我不知道的全局模式?这是另一种方法:

git rev-list --all | while read cmt
do
    cmt_tags=""
    while read tag
    do
        cmt_tags+="$(echo "$tag" | awk -F'/' '{ print ; }') "
    done <<< "$(git tag --points-at "$cmt" "integrationtest-signoff/*" "unittest-signoff/*")"
    
    test "$cmt_tags" = "integrationtest-signoff unittest-signoff " && echo "$cmt" && break
done

假设您正在标记 master 分支,我基本上遍历每个提交,直到找到具有两个标记的提交。 git tag --points-at returns 所有标签按字母顺序排列(这是我观察到的),但我不想要 / 之后的部分,所以我只使用 awk 的第一个标记. 感谢提供给 git tag 的模式,我确信它 returns 只有那些匹配模式的标签,最后我只是将 cmt_tags 字符串与预期的字符串和 break 进行比较一旦我找到一个提交。

不是很优雅,但足够简单来解决你的问题,我会说。


@torek 使用 git for-each-ref 提出了一个有趣的性能增强。前面脚本的第一行可以替换为:

git for-each-ref --format="%(committerdate)|%(objectname)" --sort=-committerdate "refs/tags/integrationtest-signoff/*" "refs/tags/unittest-signoff/*" | sort -u -r | awk -F '|' '{ print ; }' | while read cmt

现在,我不再循环提交,而是只循环特定标记的提交。当然,性能取决于有多少提交被标记为 integrationtest-/unittest-

[编辑:我可能看错了问题。请参阅 以找到一种方法来查看不同解释的答案。]

考虑将 --no-walk 标记为 git log,例如 git log --no-walk tag1 tag2

等等,我用的是-n 1,这不是一回事吗?

没有。 --no-walk-n 1 之间有天壤之别。 git log-n 参数告诉它在打印一些修订后 完全退出 。使用 -n 1git log 一显示一个特定的提交就退出。

git log 的工作方式 是这里的关键。当你 运行:

git log [options] starting-point-1 starting-point-2 starting-point-3

git log 命令将三个选定的起点提交插入到 队列(具体来说是 priority queue,尽管我们不用担心优先级部分在这里)。在名称上尝试 运行ning git rev-parse(例如分支名称、remote-tracking 名称或标签名称):

$ git rev-parse origin/maint
48bf2fa8bad054d66bd79c6ba903c89c704201f7
$ git rev-parse v2.23.0
cb715685942260375e1eb8153b0768a376e4ece7

这些哈希 ID——第二个实际上是一个 tag 哈希 ID,而不是 commit 哈希 ID,但是 git log 知道如何处理它——可以作为 git log 的“起点”。或者,给定 no 起点,git log 使用 git rev-parse HEAD 或等价物来找到要插入到此队列中的提交哈希 ID,以便队列具有只需 一个 提交即可。如果你给 git log one 提交说明符,那就是进入队列的一个提交。

一旦队列准备好了——通过你的命令行起点,或者通过git log使用HEAD——真正的工作开始了。

git log 的真正工作,即 运行 作为一个循环,一遍又一遍

此时 git log 从队列的 one commit out 开始。如果只有一个提交 in 队列,队列现在是空的。如果队列 已经 为空,git log 现在退出,因为没有什么可取出的。

从队列中取出提交后,git log 现在从 Git 保留所有提交的大数据库中取出提交。它检查提交。如果您提供 git log 选项,这些选项可能会决定是否 print 提交。如果你给了 no 选项,git log 应该现在打印提交。

如果 git log 应该打印提交,git log 现在打印提交。如果有 -n 限制,这会减少剩余计数,当它变为零时,git log 会立即退出。没有-n,或者如果计数足够大,我们继续。

在任何情况下 git log 现在有 选项 来放置提交的 parent 提交到队列。此选项是默认选项。一个 普通 提交恰好有一个 parent,因此对于大多数提交,这会将一个 parent 放入队列。一个 merge 提交有两个或更多 parents——通常只有两个——所以对于合并提交,这会将所有 parents 放入队列。

真正的工作到此结束。我们现在回到循环,以便继续使用队列。

在大多数情况下,这会产生您习惯看到的结果

假设我们有一个很好的简单线性提交字符串,结束于当前提交——即HEAD——在当前分支,像这样:

... <-F <-G <-H   <-- main (HEAD)

运行 git log 没有参数 Git 找出哪个提交是 current 提交 H.队列中有一个提交。

日志程序现在从队列中提取一个提交,队列为空。那是提交 H。它打印提交 H 的内容,并将 H 的 parent、G 放入队列。队列现在有一个提交。

日志程序现在从队列中提取一个提交,队列为空。这次是提交 G,所以 git log 打印 G 的内容,并将 G 的 parent、F 放入队列。

这对 F 重复,这导致另一个提交,git log 打印,依此类推——一直到 最先 提交,其中有 no parents。那时 git log 运行 出队并停止。

--no-walk 选项,第 1 部分

对于--no-walk,我们指示git log,在其处理队列步骤中的提交,将no parents 进入队列。如果我们将它与我们的 bog-standard git log 一起使用,当前分支是 main 并且当前提交是提交 H,那么发生的事情很简单:

  • git logHEAD,即 H,放入队列;
  • git log 从队列中弹出 H
  • git log 打印提交 H 并且不将任何内容放入队列;
  • 现在队列为空,git log 退出。

-n 1 选项相比,第 1 部分

-n 1和一个起点,没有限制ns 关于打印的内容:

  • git logHEAD,即 H,放入队列;
  • git log 从队列中弹出 H
  • git log 打印提交 H 并将其 parent G 放入队列,但打印了一个提交,因此退出。

这里的输出是一样.

比较,例如,git log --no-walk HEAD HEAD~2

这里我们给了 git log 两个 提交放入队列:HEADH,以及 HEAD~2F.

  • git log 从队列中弹出一个提交——可能是 H
  • git log 打印这一次提交,但不添加 parents;
  • git log 将剩余的提交——可能是 F——从队列中弹出;
  • git log 打印此提交,但再次不添加 parents;

队列现在是空的,所以我们打印这两个提交并退出。

使用 -n 2 行得通吗?

尝试 HEAD HEAD~2。我们从队列中的 HF 开始。让我们进一步假设队列顺序是 newest 提交总是首先打印(这是默认设置)。所以:

  • git log 从队列中弹出 H,并打印出来,然后将 G 放入队列;
  • git log 从队列中弹出 G——它是最新的,与 F 相比——并打印它;

这是允许打印的两个提交,因此它现在退出。它根本没有打印提交 F

结论:--no-walk是这个目的的flag

如果您希望 git log 仅打印 您在命令行中指定的提交 ,那么,这正是 --no-walk 的用途。将它用于其设计目的,您就完成了。

这就是我想出的纯 bash:

comm -12 <(git log --no-walk --tags=unittest-signoff --format="format:%H %ct"|sort) <(git log --no-walk --tags=integrationtest-signoff --format="format:%H %ct"|sort) | sort -k 2 -r | head -1 | cut -d ' ' -f 1

分解:

git log --no-walk --tags=unittest-signoff --format="format:%H %ct"

打印带有提交时间戳的散列。 sort 因为 comm 需要排序的输入。在 comm 找到两个标签集之间的公共哈希后,根据时间戳再次 sort 它们,最后 headcut 从两个标签中获取最近的提交集。

[编辑]还需要--no-walk

git log让你选择要看的装饰,所以这应该服务:

git log --no-walk --tags  --pretty=%H\ %d --decorate-refs=refs/tags/*-signoff \
| grep integrationtest-signoff | grep -m1 unittest-signoff