首页 > 解决方案 > 从 python 子进程调用 git-log 时忽略选项 --decorate-refs

问题描述

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

我的问题:

我从命令行运行这个命令

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*'并仅列出整个参考集

>>> 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 的目标,以便获得更广泛的答案。

我的目标是找到返回历史时找到的第一个标签(拓扑或图形排序)并且匹配正则表达式的标签。

我之前使用的是 rev-list,但没有发现文档表明这会按照我想要的顺序传递标签,也许我错过了一些东西。

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

标签: pythongitsubprocessgit-log

解决方案


添加--decorate=full或添加--decorate=short到您的git log论点。您也可以使用--decorate=trueor --decorate=1,但fullandshort是这些天记录的值。Full 包括全名(例如,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 中使用subprocess:git log是 Git 所谓的瓷器命令,这意味着它服从用户配置。如果用户有一个log.decorate设置,它会覆盖任何默认值。现在您已经了解log.decorate--decorate=参数,您可以使用--decorate=参数(它覆盖任何用户配置)在程序中强制执行正确的行为。但是还有哪些其他用户可配置的项目git log可能会破坏您的程序?未来版本的 Git怎么样,哪里git log可以获得新的配置项?什么都没有git log不幸的是,今天可以解决这个更普遍的问题,但是由于任何所谓的管道命令都无法完成某些事情- 这些命令不会根据用户配置改变行为,因此从其他程序,因为它们具有已知的、固定的输出格式——<code>git log 需要一个选项来使其表现良好。(该git status命令有这个--porcelain选项;git log只需要它自己的版本。)


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

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

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

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

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

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

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

例如,这就是您可以为一个特定的 Git 存储库安排您的user.name和/或不同的方式。user.email您在全局配置中设置这些,Git 在读取每个存储库配置之前会读取这些配置;然后在每个存储库配置中将它们设置为不同的值,这会覆盖全局配置。

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


推荐阅读