`git log --pretty`输出的条目之间保持一致分隔的格式字符串。

5
我正在尝试开发一个格式字符串来传递给git log --pretty,以便每个日志条目以完整的提交消息结尾,但是每个日志条目之间都以正好一个空行分隔。问题在于,有些完整的提交消息以换行符结尾,而有些则不是。
例如,假设我有两个提交:abc1234def5678,但只有abc1234包含完整的提交消息末尾有一个换行符。在命令行上输出原始提交内容将如下所示:
[prompt]$ git cat-file commit abc1234
(...)

Title FOO

Full commit message FOO
[prompt]$ git cat-file commit def5678
(...)

Title BAR

Full commit message BAR[prompt]$

注意新的shell提示符出现在输出的最后一行,说明提交def5678没有在完整提交消息的结尾处包含换行符。
假设def5678abc1234的父项,我想要输出一个简单的日志,每个条目仅包含短提交哈希、标题行和完整的提交消息。我可能会尝试像这样的东西:
[prompt]$ git log --graph --pretty='commit %h%n%B' abc1234
* commit abc1234
| Title FOO
| 
| Full commit message FOO
|
* commit def5678
| Title BAR
|
| Full commit message BAR
* commit <parent of def5678>
(...)

注意日志条目之间的间距。 abc1234def5678 的条目之间由一个空行隔开(除了图形字符),但是 def5678 和其父级的条目则没有。
我应该如何构造格式字符串,以便即使完整提交消息的终止不一致,间距也保持一致?mediumfullfulleremail 的内置漂亮格式已经做到了这一点,但是我想能够构造任意格式字符串来做同样的事情。
我尝试使用 %+B%-B% B 序列(以及它们的等效项 %b%n),但似乎无法获得一致的间距。
我正在使用 Git 2.17.0,如果有区别请告诉我。

你用什么工具编写提交信息?是直接使用 git 还是通过某些替代实现(即不使用 libgit2 的实现)进行提交的?如果我没记错,git 会自动修复缺少 EOL 的消息,但有些重新实现的工具则不会。 - kelvin
我并没有提交这些信息;我正在尝试阅读其他开发人员创建的git历史记录。 - eeowaa
修复所有消息是一个选项吗(这将改变所有哈希值等)?这似乎更容易做到。 - kelvin
1
如果Git有一个格式修改器,意味着“如果文本没有以换行符结尾,则添加一个换行符”,那就太好了,但它没有。(这不是答案,只是一条评论。) - torek
1
尝试使用 git log --graph --pretty='commit %h%n%B%-gd%n' 命令,因为你没有使用 reflog 选择器,所以 %gd 将会展开为空,并且 - 会吞噬所有前面的换行符。 - jthill
显示剩余4条评论
2个回答

6

正如 @jthill 在评论中所提到的,并在 git-log(1) 中表达的:

如果在占位符的 % 后面添加一个 -(减号),则在占位符扩展为空字符串时,立即删除所有连续的换行符。

因此,如果我们可以找到一个格式序列 %<token>,它总是扩展为空字符串,我们就可以使用 %-<token>%n 将零个或多个连续的换行符替换为单个换行符。实际上,确实存在这样一个格式序列:%C(),空颜色选择器。(通常括号内包含一个非空字符串,指定要在日志输出中使用的着色。有关更多详细信息,请参见 git-log(1)git-config(1)。)

%C() 评估为空字符串而不引起错误的事实似乎是一个偶然的幸运事件,而不是值得依赖的东西,但至少对于 Git 2.17 而言,它能解决问题。此时,我已经有足够的信息来回答自己的问题。

为了保持 git log --pretty=<tformat> 输出的日志条目之间的一致分隔,其中 <tformat> 可能会或可能不会评估为以换行符结尾的字符串,请在 <tformat> 后附加 %-C()%n 例如:

[prompt]$ git log --graph --pretty='commit %h%n%B%-C()%n' abc1234
* commit abc1234
| Title FOO
| 
| Full commit message FOO
|
* commit def5678
| Title BAR
|
| Full commit message BAR
|
* commit <parent of def5678>
(...)

3

来自git log手册:

-z

使用NUL而不是新的换行符分隔提交。

有了它,您就能够清晰地分离每个消息。然后,对于每个消息,仅在缺少换行符时添加行终止符

示例:

fix-eol

#!/bin/sh

lastchar="$(printf -- '%s\n' "$@" | tail -c1 | od -An -tx1)"

# output the input as is
printf -- '%s' "$@"

# print a newline only if it's missing
test "$lastchar" = ' 0a' && printf -- '\n'

使用方法:

git log -z --pretty='commit %h%n%B' | xargs -0 -n1 ./fix-eol

注意: 我无法保存缺少行终止符的提交消息(似乎git会自动修复它们),但是该脚本可以处理这样的文件。另外,根据消息的大小和内容,可能会出现问题,因为每个消息都作为参数传递(即:这是一个快速的hack)。

附言: 为了避免使用单独的脚本,可以改进示例。


这是个好主意,但不幸的是对我没用。日志条目以 --commit <hash> 开始,而且这些条目仍然存在间距问题。此外,需要稍微修改你的 fix-eol 脚本,使其与 --graph 选项兼容。尽管如此,这是一个很好的基础,可以进行调试... 我会在几个小时后回来处理。 - eeowaa
日志条目以“--commit <哈希值>”开头。已修正;我之前复制了错误的版本。 - kelvin
"使其与--graph选项兼容" 图形绘制是否会因每个损坏的消息而移动,还是仅在最后一个消息上移动?如果是前者,我认为没有一种简单(或保证)的方法来做到这一点。 - kelvin
你的修复确实从每个日志条目中删除了前导的“--”,但我仍然发现间距是一个问题。我甚至尝试了不同的平台(Linux而不是Cygwin)和Git 2.17.2而不是2.17.0。关于“--graph”选项,那实际上从来没有成为问题。我只是被前导的“--”字符搞混了。对于错误信息,我很抱歉。 - eeowaa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接