GitPython 的“git show”输出出现“文件不是 zip 文件”错误

tok*_*a-n 4 python git plugins

重现问题的脚本

将此代码保存为 shell 脚本并运行它。该代码应该报告File is not a zip file错误。

#!/bin/bash

set -eu

mkdir foo
cd foo

pip install --user GitPython

echo foo > a
zip a.zip a

# -t option validates the zip file.
# See https://unix.stackexchange.com/questions/197127/test-integrity-of-zip-file
unzip -t a.zip

git init
git add a.zip
git commit -m 'init commit'

cat << EOF > test.py
from git import Repo
import zipfile
from io import StringIO

repo = Repo('.', search_parent_directories=True)

raw = repo.git.show("HEAD:a.zip")

z = zipfile.ZipFile(StringIO(raw), "r")
EOF

python3 test.py
Run Code Online (Sandbox Code Playgroud)

原始问题

我正在编写一个 Krita 插件,用于查看 Git 存储库以前提交的文件,并且我想获取 Krita 文件的缩略图文件。为此,我尝试使用 获取文件git show,将其解压缩,因为 Krita 文件是Zip 文件,然后获取preview.pngmergedimage.png

%unzip image.kra
Archive:  image.kra
 extracting: mimetype                
  inflating: maindoc.xml             
  inflating: documentinfo.xml        
  inflating: preview.png             
  inflating: image/layers/layer2     
  inflating: image/layers/layer2.defaultpixel  
  inflating: image/layers/layer2.icc  
  inflating: image/annotations/icc   
  inflating: mergedimage.png         
  inflating: image/animation/index.xml  
Run Code Online (Sandbox Code Playgroud)

我们可以像.kra使用 GitPython 一样从 Git 存储库获取文件。但是,我无法像它所说的那样解析该文件。(此代码基于此 SO 答案strzipfile.ZipFileFile is not a zip file

%unzip image.kra
Archive:  image.kra
 extracting: mimetype                
  inflating: maindoc.xml             
  inflating: documentinfo.xml        
  inflating: preview.png             
  inflating: image/layers/layer2     
  inflating: image/layers/layer2.defaultpixel  
  inflating: image/layers/layer2.icc  
  inflating: image/annotations/icc   
  inflating: mergedimage.png         
  inflating: image/animation/index.xml  
Run Code Online (Sandbox Code Playgroud)

会发出

Traceback (most recent call last):
  File "/home/hiroki/krita_question/test.py", line 11, in <module>
    z = zipfile.ZipFile(StringIO(raw), "r")
  File "/usr/lib/python3.9/zipfile.py", line 1257, in __init__
    self._RealGetContents()
  File "/usr/lib/python3.9/zipfile.py", line 1324, in _RealGetContents
    raise BadZipFile("File is not a zip file")
zipfile.BadZipFile: File is not a zip file
Run Code Online (Sandbox Code Playgroud)

git show我相信这是一个有效的 Krita 文件,因为我可以在命令行上恢复该文件。所以,

%git show HEAD~:image.kra > prev.kra
%krita prev.kra
Run Code Online (Sandbox Code Playgroud)

工作正常。解压缩文件也有效。

为什么我无法将输出解析git show为 Zip 文件?

git log --stat|grep -v 'Author'

commit b96d915862b39a204a9f4350e7e56634b6fcfe0b
Date:   Wed Mar 30 14:44:02 2022 +0900

    chore: add

 ls | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 231 insertions(+)

commit 619984a842c6c2daf31559c1979f91227a323648
Date:   Wed Mar 30 14:43:58 2022 +0900

    chore: add

 image.kra | Bin 0 -> 777685 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
Run Code Online (Sandbox Code Playgroud)

版本

蟒蛇:3.9.9

GitPython:3.1.27

克里塔:5.0.2

Linux 5.15.16-gentoo

所有文件都是在 Linux 中创建的。

tok*_*a-n 5

更新

GitPython 版本 3.1.28(尚未发布)应该添加strip_newline_in_stdout选项. 如果该选项设置为False,则将保留\n运行的任何命令的标准输出的尾部。repo.git.foobar

raw = repo.git.show("HEAD~:image.kra", strip_newline_in_stdout=False)
Run Code Online (Sandbox Code Playgroud)

原答案

看来这是由GitPython 的 bug引起的。它截断了最后\n的输出git show并使文件无效。

我更改了使用的代码subprocess.PopenZipFile成功了。

import zipfile
from io import BytesIO
import subprocess

p = subprocess.Popen(["git", "show", "HEAD:a.zip"], stdout = subprocess.PIPE)

out, _ = p.communicate()

z = zipfile.ZipFile(BytesIO(out), "r")
Run Code Online (Sandbox Code Playgroud)