我如何过滤 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 1
,git 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 log
将 HEAD
,即 H
,放入队列;
git log
从队列中弹出 H
;
git log
打印提交 H
并且不将任何内容放入队列;
- 现在队列为空,
git log
退出。
与 -n 1
选项相比,第 1 部分
有-n 1
和一个起点,没有限制ns 关于打印的内容:
git log
将 HEAD
,即 H
,放入队列;
git log
从队列中弹出 H
;
git log
打印提交 H
并将其 parent G
放入队列,但打印了一个提交,因此退出。
这里的输出是一样.
比较,例如,git log --no-walk HEAD HEAD~2
这里我们给了 git log
两个 提交放入队列:HEAD
或 H
,以及 HEAD~2
或 F
.
git log
从队列中弹出一个提交——可能是 H
;
git log
打印这一次提交,但不添加 parents;
git log
将剩余的提交——可能是 F
——从队列中弹出;
git log
打印此提交,但再次不添加 parents;
队列现在是空的,所以我们打印这两个提交并退出。
使用 -n 2
行得通吗?
尝试 HEAD HEAD~2
。我们从队列中的 H
和 F
开始。让我们进一步假设队列顺序是 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
它们,最后 head
和 cut
从两个标签中获取最近的提交集。
[编辑]还需要--no-walk
git log
让你选择要看的装饰,所以这应该服务:
git log --no-walk --tags --pretty=%H\ %d --decorate-refs=refs/tags/*-signoff \
| grep integrationtest-signoff | grep -m1 unittest-signoff
在我提交到 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 1
,git 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 log
将HEAD
,即H
,放入队列;git log
从队列中弹出H
;git log
打印提交H
并且不将任何内容放入队列;- 现在队列为空,
git log
退出。
与 -n 1
选项相比,第 1 部分
有-n 1
和一个起点,没有限制ns 关于打印的内容:
git log
将HEAD
,即H
,放入队列;git log
从队列中弹出H
;git log
打印提交H
并将其 parentG
放入队列,但打印了一个提交,因此退出。
这里的输出是一样.
比较,例如,git log --no-walk HEAD HEAD~2
这里我们给了 git log
两个 提交放入队列:HEAD
或 H
,以及 HEAD~2
或 F
.
git log
从队列中弹出一个提交——可能是H
;git log
打印这一次提交,但不添加 parents;git log
将剩余的提交——可能是F
——从队列中弹出;git log
打印此提交,但再次不添加 parents;
队列现在是空的,所以我们打印这两个提交并退出。
使用 -n 2
行得通吗?
尝试 HEAD HEAD~2
。我们从队列中的 H
和 F
开始。让我们进一步假设队列顺序是 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
它们,最后 head
和 cut
从两个标签中获取最近的提交集。
[编辑]还需要--no-walk
git log
让你选择要看的装饰,所以这应该服务:
git log --no-walk --tags --pretty=%H\ %d --decorate-refs=refs/tags/*-signoff \
| grep integrationtest-signoff | grep -m1 unittest-signoff