在我提交到master之后,我运行了几个标记提交的CI,它们成功完成了提交。假设我有两个CI单元测试和集成测试,它们都在提交时创建标记。假设unittest-signoff/{number}
和integrationtest-signoff/{number}
,其中{number}是自动递增数,以确保唯一性。
如果我执行git log -n1 --tags="unittest-signoff"
,这将给我最近的提交,它已经从单元测试中注销。与git log -n1 --tags="integrationtest-signoff"
相同
我的问题是,什么命令会给我同时具有两个标记的最新提交。
git log
允许您选择要查看的装饰,因此这应该是:
git log --no-walk --tags --pretty=%H %d --decorate-refs=refs/tags/*-signoff
| grep integrationtest-signoff | grep -m1 unittest-signoff
我本以为git会为此做好准备,但我没有找到。尽管如此,我仍然相信我不知何故错过了它,也许是使用了一些我不知道的glob模式?这是另一种方法:
git rev-list --all | while read cmt
do
cmt_tags=""
while read tag
do
cmt_tags+="$(echo "$tag" | awk -F'/' '{ print $1; }') "
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
按字母顺序返回所有标签(这就是我观察到的),但我不想要/
之后的部分,所以我只使用awk
的第一个标记。由于提供给git tag
的模式,我确信它只返回那些与模式匹配的标签,最后我只需将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 $2; }' | while read cmt
现在,我不再循环提交,而是只循环特定的标记提交。当然,性能取决于有多少提交被标记为integrationtest-/unittest-
。
[编辑:我可能误解了这个问题。请参阅Marco Luzzara的答案,找到一种方法来查看不同解释的答案。]
考虑--no-walk
标志到git log
,例如git log --no-walk tag1 tag2
。
等等,我用的是-n 1
,不是一样的吗
没有。CCD_ 19和CCD_。git log
的-n
参数告诉它在打印一些修订后完全退出。对于-n 1
,一旦git log
显示一个特定的提交,它就会退出。
git log
的工作方式是这里的关键。运行时:
git log [options] starting-point-1 starting-point-2 starting-point-3
git log
命令将三个选定的起始点提交插入队列(特别是优先级队列,尽管我们在这里不担心优先级部分)。尝试在名称(例如分支名称、远程跟踪名称或标记名称)上运行git rev-parse
:
$ git rev-parse origin/maint
48bf2fa8bad054d66bd79c6ba903c89c704201f7
$ git rev-parse v2.23.0
cb715685942260375e1eb8153b0768a376e4ece7
这些散列ID——第二个实际上是标记散列ID,而不是提交的散列ID,但git log
知道如何处理它——可以充当";起点";对于CCD_ 29。或者,给定没有起始点,git log
使用git rev-parse HEAD
或等效方法来查找要插入到该队列中的提交哈希ID,这样队列中只有一个提交。如果您为git log
提供一个提交说明符,则该提交就是进入队列的提交。
一旦队列准备就绪——通过命令行起始点,或者通过使用HEAD
的git log
——真正的工作就开始了。
git log
的实际工作,作为一个循环,一遍又一遍地运行
此时,git log
开始从队列中取出一个提交。如果队列中只有一个提交,那么该队列现在是空的。如果队列已经为空,git log
现在退出,因为没有什么可取出的。
从队列中取出提交后,git log
现在从Git保存所有提交的大数据库中取出提交。它检查提交。如果您提供了git log
选项,这些选项可能会决定是否打印提交。如果您给没有选项,git log
现在应该打印提交。
如果git log
应该打印提交,git log
现在打印提交。如果存在-n
限制,则会递减剩余计数,当其变为零时,git log
会立即退出。如果没有-n
,或者数量足够大,我们就会继续前进。
在任何情况下git log
现在都有选项将提交的父提交放入队列。此选项是默认选项。普通提交只有一个父级,因此对于大多数提交,这会将一个父项放入队列中。合并提交有两个或多个父级,通常只有两个,因此对于合并提交,这会将所有父级放入队列中。
这就完成了真正的工作。现在我们回到循环,以便继续处理队列。
在大多数情况下,这会产生你习惯看到的东西
假设我们有一个很好的简单线性提交字符串,以当前提交结束,即HEAD
,位于的当前分支上,如下所示:
... <-F <-G <-H <-- main (HEAD)
在没有参数的情况下运行git log
可以让Git确定哪个提交是当前的提交,即提交H
。队列中有一个提交。
日志程序现在从变为空的队列中提取一个提交。那就是提交H
。它打印提交H
的内容,并将H
的父级G
放入队列中。队列中现在有一个提交。
日志程序现在从变为空的队列中提取一个提交。这一次是提交G
,所以git log
打印G
的内容,并将G
的父级F
放入队列中。
对于F
重复此操作,这将导致返回另一个提交,git log
将打印该提交,依此类推——一直到第一个提交,该提交没有父级。此时,git log
已超出队列并停止。
--no-walk
选项,第1部分
使用--no-walk
,我们指示git log
在其处理队列外提交步骤中,将无父项放入队列。如果我们将其与我们的沼泽标准git log
一起使用,当前分支为main
,当前提交为提交H
,那么会发生什么很简单:
git log
将HEAD
,即H
放入队列git log
将H
弹出队列git log
打印提交H
,并且不向队列中放入任何内容- 并且队列现在是空的并且CCD_ 75退出
与-n 1
选项的比较,第1部分
有-n 1
和一个起点,对打印内容没有限制:
git log
将HEAD
,即H
放入队列git log
将H
弹出队列git log
打印提交H
并将其父G
放入队列,但已打印一个提交,因此退出
此处的输出与相同。
与例如git log --no-walk HEAD HEAD~2
进行比较
在这里,我们给了git log
两个提交以放入队列:HEAD
或H
,以及HEAD~2
或F
。
git log
从队列中弹出其中一个提交——可能是H
git log
打印此提交,但不添加父项git log
从队列中弹出剩余的提交——可能是F
git log
打印此提交,但再次不添加父项
并且队列现在是空的,所以我们打印这两个提交并退出。
使用-n 2
是否有效
试试HEAD HEAD~2
。我们从队列中的H
和F
开始。让我们进一步假设队列顺序是这样的,最新的提交总是首先打印(这是默认的)。因此:
git log
将H
弹出队列并打印出来,将G
放入队列git log
从队列中弹出G
——与F
相比,它是最新的——并打印它
这是允许打印的两个提交,因此它现在退出。它根本没有打印提交F
!
结论:--no-walk
是这方面的标志
如果您希望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"
打印带有提交时间戳的哈希。CCD_ 112,因为CCD_。在comm
找到两个标签集之间的公共散列后,根据时间戳再次sort
,最后head
和cut
从两个标签集中获得最近的提交。
[编辑]仍需--no-walk