"git log --pretty=<pretty format>"是一个瓷器命令还是管道命令?

12

我正在创建一些脚本和程序,使用它们获取提交信息。

git log --pretty=<my format> -1 <commit>

我想知道这个命令的输出是否适合被程序解析(plumbing),还是只是为了呈现给人类查看(porcelain)。例如,在某些项目中,我用以下命令获取提交SHA+作者名称+提交摘要:

git log --pretty="%H%n%an%n%s" -1 HEAD

然后我按换行符(我在Linux系统上)拆分输出字符串。

此外,在某些情况下,我也会做这样的事情:

git log --pretty='[%h] %an: %s' -1 HEAD

接下来使用以下正则表达式解析结果,期望在捕获的组中包含短SHA、作者名称和提交摘要:

^\[(\w+)\] ([^:]+): (.*)$

这是一个好的方法吗?如果不是,那么编程获取提交信息的首选方式是什么?


我更喜欢使用瓷器命令,因为这个(离题)线索:在Pro Git v2中,第10.1章说“本书的前九章几乎完全涉及瓷器命令”,而git log以面向机器的格式出现在第2.3章,它是“本书的前九章”之一。 - Geno Chen
4个回答

7

git log 是一个瓷质命令。

实际上,它执行了相当多的任务——包括遍历修订图、git diffgit grep 等等。

一种类似管道的方法来执行某些操作。

git log --pretty='[%h] %an: %s' -1 HEAD

git show-refgit cat-file结合起来,并解析结果,就像这样:
git cat-file commit `git show-ref -s HEAD` |
  while read line; do
    # do some processing
  done

实际上,Git的根手册页git(1) - 运行git help git阅读它 - 包含将命令分解为瓷器和管道层的说明。


“git cat-file commit <ref>” 似乎是一个可靠的管道命令,但输出结果似乎比“git log --pretty”的解析更困难。有更好的解决方案吗? - iBug
1
我看不出这有什么复杂的地方:它是由形如“^key SP value LF$”的行头组成,通过一个空行“^LF LF$”与内容分隔开来。因此,基本上你需要读取所有行,直到遇到一个空行,并查找特定的关键字,比如“author”和/或“committer”。我的意思是,你能详细说明一下你在处理它时遇到了什么具体困难吗? - kostix
你好,能否看一下我的回答并给予一些评论?(对你的回答点赞) - iBug

4

我同意kostix的观点;git log是一个外壳命令。但问题在于,有些事情git log可以做,而其他命令很难做到,因此我们有时可以让git log“像”管道命令一样运作。

当比较例如git branchgit taggit for-each-ref,或者git diffgit diff-treegit diff-filesgit diff-index时,管道和外壳之间的关键区别显现出来。这不是指每个管道有多少个外壳。例如,在这里,管道git for-each-ref有两个单独的外壳前端,而单一前端git diff有三个管道后端。关键是,git diff基于用户选择的配置项改变其行为

diff.algorithm
diff.dirstat
diff.renameLimit
diff.renames
diff.statGraphWidth
diff.submodule

等等。管道版本忽略所有用户配置,因此您编写的脚本对Alice、Bob、Carol和Dave都表现出相同的行为,即使他们有不同的设置。

使用此定义时,我们可以决定git log是否像一个管道命令一样运行。这需要枚举所有git log配置选项。不幸的是,没有清洁的方法来做到这一点-更多的选项可以随时添加,并且随着时间的推移,一些选项已经添加了。

以下是我通过扫描git loggit config手册找到的列表。请注意,我省略了所有与差异相关的内容(例如,color.diff和上面提到的diff.*项),因为有管道命令来处理git log中等效的-p(尽管您必须逐个提交地进行工作)。

color.decorate.<slot>
core.notesRef
format.pretty
i18n.logOutputEncoding
log.abbrevCommit
log.date
log.decorate
log.follow
log.graphColors
log.mailmap
log.showRoot
log.showSignature
notes.displayRef
pretty.<name>

假设我们想要获取某个特定提交的提交日期,并以某种特定的格式进行格式化。为了做到这一点,我们可以运行:

git log --no-walk --pretty=format:%cd

我们可以在主要的git log文档中找到这样描述pretty format %cd的方法:

%cd: 提交者日期(格式遵循--date=选项)

如果我们没有给出--date=选项,那么git log会查找log.date设置。这是一个用户配置选项,我们的git log输出将取决于用户的选择,而不是我们的选择。
要使这个git log像一个plumbing命令一样工作,我们必须使用--date=default-c log.date=default来覆盖log.date配置设置。
git -c log.date=default log --no-walk --pretty=format:%cd

或者:

git log --no-walk --date=default --pretty=format:%cd

理想情况下,Git 应该有一个被定义为 git log 的管道变量的 plog 命令,或者一个 git format-log-metadata 管道命令,它接受 --pretty= 选项并格式化日志元数据。由于没有这个命令,所以任何需要 git log --pretty=format:... 输出的脚本编写者都需要确保他们知道可能会影响他们的配置选项。

这是否意味着,即使它不是严格的管道命令,我仍然可以覆盖相关设置并期望获得管道结果? - iBug
@iBug:是的。问题在于,由于git log确实是一个外壳程序,所以将来可能会有人添加新的配置项,你现在还不知道必须要覆盖它,因为现在你还不需要覆盖它。 - torek
你好,能否看一下我的回答并给予一些评论?(对你的回答点赞) - iBug

0
感谢kostic和torek的回答。
尽管他们的答案不同,但我认为某些漂亮格式选项可以安全地被视为管道(即可以被程序解析)。例如:
  • %H 表示完整的提交 SHA
  • %T 表示完整的树 SHA
  • %P 表示完整的父 SHA
  • %an, %cn, %ae, %ce, %at%ct 表示作者/提交者姓名/电子邮件/日期(Unix)。同时RFC 2822和ISO 8601风格的时间是可靠的,如%aD%cD%aI%cI
  • %s 表示提交摘要
  • %G? 表示签名状态
  • %n 表示换行符(哈哈...)

是的,虽然像%ad%cN这样的格式说明符可能会受到用户设置的影响,但上述那些不太可能会受到影响。因此,我已经决定,使用上述说明符组合的漂亮格式来解析git log输出的当前代码是安全且不易出错的。


@jthill 它与 git log 有何不同之处? - iBug
1
它具有核心命令(也称为管道保证),其输出旨在供您使用。 - jthill
1
使用--pretty={,t}format / --format=指令似乎是相当安全的,是吗?有趣的是,尽管这些指令大多数情况下可以与git rev-list一起使用,但它们并未列为其选项。但是,rev-list和log是从同一源文件构建的,并且大多数情况下处理相同的参数。不幸的是,当使用--format时,git rev-list的行为表现不佳! - torek
@torek 对于我来说,匹配行中的 ^commit [0-9a-f]{40}$ 并将其丢弃是零难度的。 - iBug
1
当然,但是再说一遍,你为什么必须这样做呢?这就像让git for-each-ref在每行上打印日期一样。rev-list是一个管道命令;它应该像一个管道命令一样运作。(另外,如果/当Git切换到SHA3-256时,那个{40}将是错误的。) - torek
显示剩余2条评论

0

仅补充 kostix的答案:对我来说,git show-ref -s HEAD 没有输出并返回退出码 1。

可以尝试使用:

git cat-file commit `git show-ref -s HEAD`

我使用:

git cat-file commit `git show-ref --head -s HEAD`

或者:

git cat-file commit `git rev-parse HEAD`

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