从 python 子进程调用 git-log 时选项 --decorate-refs 被忽略

option --decorate-refs is ignored when calling git-log from python subprocess

我被这个错误困住了。我试图搜索一堆东西,我尝试使用调试器跟踪调用。我none更聪明。

我的问题:

我从命令行运行这个命令

git log --format=format:%D --simplify-by-decoration --decorate-refs=*platVer*

我得到了预期的标签列表

tag: platVer/222.3.4123, tag: myplatVer-222.3.4123
tag: platVer-20.07.000
tag: platVer-20.06.000
tag: platVer-20.05.000

如果我在命令行上从python运行这个,我也会得到预期的列表

>>> from subprocess import call, Popen, PIPE
>>> pp = Popen(['git', 'log', '--decorate-refs=*platVer*', '--format=format:%D', '--simplify-by-decoration'])
tag: platVer/222.3.4123, tag: myplatVer-222.3.4123
tag: platVer-20.07.000
tag: platVer-20.06.000
tag: platVer-20.05.000

运行 此行在空闲或脚本中未捕获输出(如预期),要启用标准输出的捕获,popen 需要将标准输出参数设置为 PIPE。

但是如果我 运行 使用 stdout=PIPE,它似乎会忽略 '--decorate-refs=*platVer*' 并且只列出整组 refs

>>> pp = Popen(['git', 'log', '--decorate-refs=*platVer*', '--format=format:%D', '--simplify-by-decoration'], stdout=PIPE)
>>> pp.stdout.read()
b'HEAD -> feature/ps2python, origin/feature/ps2python\ntag: platVer/222.3.4123, tag: myplatVer-222.3.4123, tag: mao_test ....

当我从脚本或空闲时 运行 得到同样的结果。

from subprocess import Popen, PIPE

pp = Popen(['git', 'log', '--decorate-refs=*platVer*', '--format=format:%D', '--simplify-by-decoration'], stdout=PIPE)
print( pp.stdout.read().decode('ascii' ) )

给我这个

HEAD -> feature/ps2python, origin/feature/ps2python
tag: platVer/222.3.4123, tag: myplatVer-222.3.4123, tag: mao_test
show-current, develop
tag: platVer-20.07.000,
... (cut the remaining many many lines of refs)

我正在 运行宁 windows 10(版本 10.0.18363.778) git 版本 2.29.2.windows.2 python 版本 3.8.5

我试过 shell=Tre/False, universal_newlines=True/False 我在 WSL 中试过了 (ubuntu) 都给出了相同的结果

然后我在虚拟 ubuntu 18.LTS 中尝试了。使用 git 版本 2.17。在这里我得到了有线结果,其中 '--decorate-refs=*platVer*' 被忽略了。从命令行。

然后我在这个 ubuntu 上将 git 更新到较新的版本 (2.29.2)。现在命令完全按预期工作....

然后我尝试了来自 python 的相同命令,结果与在 win10 机器上相同。

请帮忙。无法弄清楚设置 stdout=PIPE 如何改变 git 命令的行为。

编辑: 我确实检查过使用和不使用 PIPE

时调用的 git 版本相同

编辑2:

我将@torek 的回答标记为已接受,因为它完美地解决了我的问题。

不过,我应该说明我使用 git-log 的目的是为了获得更广泛的答案。

My goal is to find the tag that is the first tag found when traveling back in history (topological or graph ordering) and that matches a regular expression.

我以前使用过 rev-list,但没有发现任何文档表明这会按我想要的顺序传送标签,也许我错过了什么。

当我同时声明我需要正则表达式匹配时,我在我的命令中使用简单的 glob 模式的原因是我假设 globbing 更快,因此将它用作预过滤器以缩短python中需要正则表达式解析的列表。我希望标签列表在几年内包含 1000 多个标签并且还在增长。其中带有单词 'platVer' 的标签将占该列表的 1% 左右。

--decorate=full--decorate=short 添加到您的 git log 参数中。您也可以使用 --decorate=true--decorate=1,但现在 fullshort 是记录值。完整包括全名(例如,refs/heads/somebranch),而 short 缩写为分支或标签名称。

很长(但可选)有用的背景信息

默认的 log.decorate 设置是 auto(从 Git 2.9 开始;之前是 no/0/false , 并且在不同的地方引入了各种错误,然后在以后的版本中修复;它自 Git 2.13) 以来一直稳定。 auto 设置意味着 short 如果人类正在读取输出,no 如果程序正在读取输出 .1

--simplify-by-decoration --decorate-refs=... 需要装饰本身(即必须打开)才能工作。可能这些选项中的任何一个都应该暗示 --decorate=short 如果它目前仍然 auto 在 Git 配置中未设置。2

这一切都指向以编程方式使用 git log 的更普遍的问题,例如,从 Python 和 subprocessgit log 是 Git 调用的内容porcelain命令,这意味着它服从用户配置。如果用户有 log.decorate 设置,它将覆盖任何默认值。现在您了解了 log.decorate--decorate= 参数,您可以使用 --decorate= 参数(它会覆盖任何用户配置)在程序中强制执行正确的行为。但是 other 用户可配置的项目存在于 git log 中会破坏你的程序吗? Git 的未来版本,其中 git log 可能会获取新的配置项?不幸的是,今天 对这个更普遍的问题无能为力,但由于 git log 所做的某些事情不能由任何所谓的 [=83] =]plumbing 命令——这些命令 不会 根据用户配置更改行为,因此对于其他程序很有用,因为它们具有已知的固定输出格式—git log 需要一个选项来让它表现良好。 (git status 命令对此有 --porcelain 选项;git log 只需要它自己的版本。)


1Git 实际上 不知道 是否有人正在阅读输出。相反,它通过检查标准输出流来近似:如果标准输出(文件描述符 1)响应 isatty C 库调用的真值,或者 git log 输出被馈送到寻呼机,它假设一个人正在阅读输出。在 subprocess 中使用管道意味着 stdout 是 而不是 tty,默认情况下它也会禁用寻呼机。但是,有一个强制使用寻呼机的用户配置设置:请参阅“更一般的问题”段落。

2一般来说,Git配置的工作方式是这样的:

  • 首先,程序设置任何自动默认值,例如log.decorate=auto(这通常只是开放编码,而不是使用配置机制)。

  • 接下来,Git读取系统配置文件。如果其中有 log.decorate=short 之类的设置,则应用该设置,覆盖自动默认值。 (这通常通过回调,从配置机制到程序。)

  • 接下来,Git读取您的个人全局配置文件。如果其中有 log.decorate=auto 等设置,则应用该设置。如果之前的配置有设置,这将覆盖之前的设置。

  • 接下来,Git 读取此特定 Git 存储库的配置文件。如果它有诸如 log.decorate=full 之类的设置,则应用该设置,并像以前一样覆盖任何以前的设置。

  • 最后,Git 应用命令行参数设置。因此,这些会覆盖在前面任何步骤中选择的任何设置。

例如,您可以这样安排 user.name and/or user.email 对于一个特定的 Git 存储库来说是不同的。您在全局配置中设置这些,Git 在读取每个存储库配置之前读取;然后在每个存储库配置中将它们设置为不同的值,并覆盖全局配置。

在 Git 的较新版本中,您还可以设置 每个工作树配置: git config --worktree。这是在每个存储库配置文件之后读取的,但在命令行参数之前使用,因此它具有第二高的优先级。要使每个工作树设置生效,您必须启用 extensions.worktreeConfig。在这里要小心,因为这个扩展有一段时间存在一些错误。