如何使用命令行工具进行DEFLATE压缩以提取git对象?

91
我正在寻找一个DEFLATE算法的命令行包装器。我有一个使用DEFLATE压缩的文件(git blob),我想解压它。gzip命令似乎没有直接使用DEFLATE算法的选项,而不是gzip格式。理想情况下,我正在寻找一个可以做到这一点的标准Unix / Linux工具。编辑:当我尝试使用gzip解决我的问题时,这是我得到的输出:
$ cat .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 | gunzip

gzip: stdin: not in gzip format

针对那些通过搜索引擎找到这个问题并想要使用 cURL 解压数据的人的相关问题:https://dev59.com/bWsy5IYBdhLWcg3wtwW9 - baptx
22个回答

53

您可以使用OpenSSL命令行工具来完成此操作:

openssl zlib -d < $IN > $OUT

不幸的是,至少在 Ubuntu 上,默认的构建配置 (--no-zlib--no-zlib-dynamic) 禁用了 zlib 子命令,因此您需要从源代码编译 openssl 才能使用它。但是,在 Arch 上默认情况下启用它。

编辑:看起来在 Arch 上也不再支持 zlib 命令了。这个答案可能已经没有用了 :(


13
请注意,如果您的 OpenSSL 配置选项包括“--no-zlib”和“--no-zlib-dynamic”,即默认选项,则不会提供“zlib”子命令(以及“enc”子命令的“-z”选项)。因此,只有在您的 OpenSSL 编译时移除了这些配置选项中的一个的“no-”前缀时,本答案才适用。您可以通过查看“openssl version -f”输出中是否存在“-DZLIB”来确定。 - Hercynium
@Hercynium 谢谢!特别是对于Ubuntu 14.04来说是这种情况 :( - Ciro Santilli OurBigBook.com
同样适用于Mac。 - Ben
3
在 Mac 上使用 LibreSSL 2.2.7 无法正常工作。我收到了 openssl:Error: 'zlib' is an invalid command. 的错误提示。 - prayagupa
1
这也适用于Windows,在git bash shell中使用捆绑的openssl。 - codeape

53

类似下面的代码将打印原始内容,包括"$type $length\0"头部:

perl -MCompress::Zlib -e 'undef $/; print uncompress(<>)' \
     < .git/objects/27/de0a1dd5a89a94990618632967a1c86a82d577

1
[错误地] 在原始deflate流中没有78标记和最终CRC的情况下产生空输出和零退出码。 - ulidtko
这个答案非常棒,我也可以用zlib直接压缩C的任何数据,所以很好用。而且通常情况下,大多数世界问题都可以通过一个PERL一行代码解决 ;) - Mecki

42

Pythonic一行代码:

$> python -c "import zlib,sys;print \
           repr(zlib.decompress(sys.stdin.read()))" < $IN

repr(...) 似乎会将所有内容用引号 ('...') 包裹起来,所以我不得不将其删除(解压缩一个 zlib 压缩的 JSON 文件)。 - Adam Lindberg
1
实际上,如果你在Python 3中期望一个utf8文件,那么它是python -c“import zlib,sys;print(zlib.decompress(sys.stdin.buffer.read()).decode('utf8'))”< $IN - Cyrille Pontvieux

38

更新:Mark Adler指出,git blob不是原始的DEFLATE流,而是zlib流。这些可以通过pigz工具解压缩,该工具预装在几个Linux发行版中:

$ cat foo.txt 
file foo.txt!

$ git ls-files -s foo.txt
100644 7a79fc625cac65001fb127f468847ab93b5f8b19 0   foo.txt

$ pigz -d < .git/objects/7a/79fc625cac65001fb127f468847ab93b5f8b19 
blob 14file foo.txt!

Git Bash for Windows用户会发现默认情况下无法使用pigz。您可以在此处找到预编译的32/64位版本。我尝试了64位版本,效果很好。例如,您可以直接将pigz.exe复制到c:\Program Files\Git\usr\bin,以便将其放在路径上。
Homebrew和Macports都提供了pigz,因此您可以使用brew install pigzsudo port install pigz进行安装(如果您还没有它,则可以按照官网上的说明安装Homebrew)。

出于历史原因,以下是我的原始答案:

如果我理解Marc van Kempen提到的Wikipedia文章中的提示,您可以直接使用zlib中的puff.c

这是一个小例子:

#include <assert.h>
#include <string.h>
#include "puff.h"

int main( int argc, char **argv ) {
    unsigned char dest[ 5 ];
    unsigned long destlen = 4;
    const unsigned char *source = "\x4B\x2C\x4E\x49\x03\x00";
    unsigned long sourcelen = 6;    
    assert( puff( dest, &destlen, source, &sourcelen ) == 0 );
    dest[ 4 ] = '\0';
    assert( strcmp( dest, "asdf" ) == 0 );
}

4
好的,我会尽力进行翻译。内容如下:我看过那个了,但我更喜欢常见的打包工具。 - Felix Geisendörfer
好的,现在进行了非常晚的编辑,并提供了一个可工作的最小实例。 - mkluwe
4
这种方法行不通。Git中的blob是zlib流,而不是原始的deflate压缩。这个解决方案适用于原始的deflate压缩。Puff不处理zlib头和尾部。如果你需要一个工具,可以使用pigz,它可以通过-dz选项解压缩zlib格式,也可以通过-z选项生成zlib格式。 - Mark Adler
1
@MarkAdler -z, --zlib 压缩成 zlib 格式(.zz),而不是 gzip 格式。目前此标志仅适用于压缩,不适用于解压缩。 pigz -d < "infile" > "outfile" 可以正常工作。 - murla
@mkluwe,希望你不介意我为Windows Git Bash用户添加了关于pigz的信息。这个答案仍然是正确的,对我非常有用,我只是想进一步改进它。 - kriegaex
有趣的是,这对我起了作用,但是省略了<(标准输入重定向)会导致它失败,并显示pigz: skipping: .git/objects/12/2898... does not have compressed suffix - undefined

28
您可以像这样使用zlib-flate:
cat .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 \
    | zlib-flate -uncompress; echo

如果你需要安装,它默认已经在我的机器上了,但它是 qpdf - 用于转换和检查PDF文件的工具 的一部分。

我在命令的结尾加上了一个echo,因为这样输出更容易阅读。


6
无需猫:zlib-flate -uncompress < .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 - G. Sylvie Davies

27
尝试以下命令:
printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - .git/objects/c0/fb67ab3fda7909000da003f4b2ce50a53f43e7 | gunzip

No external tools are needed.

来源:unix SE上的如何在UNIX中解压缩zlib数据?


1
你最终会遇到一个“文件意外结尾”错误,但仍然是个好的hack。 - Eric
3
加上gzip文件头,很不错 :) - Jaap Versteegh
1
这就是我也发现的地方 - 我在我的.bashrc中添加了zlipd() (printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" |cat - $@ |gzip -dc),现在 :) - Tobias Kienzler
不错的技巧!@Eric 添加 2> /dev/null 将 stderr 发送到 null。 - poe84it

14

这里是一个Ruby单行代码(首先进入.git/目录并确定任何对象的路径):

ruby -rzlib -e 'print Zlib::Inflate.new.inflate(STDIN.read)' < ./74/c757240ec596063af8cd273ebd9f67073e1208

去掉[blob size]头部ruby -rzlib -e 'print Zlib::Inflate.inflate($stdin.read).split("\x00")[1..-1].join' < .git/objects/abc - yachi

12

我厌倦了没有一个好的解决方案,所以我在NPM上发布了一个东西:

https://github.com/jezell/zlibber

现在只需将其与inflate / deflate命令一起使用即可。


你如何使用这个包? - RHPT
1
在Windows上,执行“type #### | inflate”命令,其中####是对象的校验和。 - mhenry1384
或者 inflate < 文件名 - Andrei Damian-Fekete

10
这是一个在Python中打开提交对象的示例:
$ git show
commit 0972d7651ff85bedf464fba868c2ef434543916a
# all the junk in my commit...
$ python
>>> import zlib
>>> file = open(".git/objects/09/72d7651ff85bedf464fba868c2ef434543916a")
>>> data = file.read()
>>> print data
# binary garbage
>>> unzipped_data = zlib.decompress(data)
>>> print unzipped_data
# all the junk in my commit!

你将看到的几乎与“git cat-file -p [hash]”命令的输出完全相同,除了该命令不会打印标题(“commit”后跟内容大小和一个空字节)。

4
根据操作系统的不同,你可能需要在 open 函数中添加 "rb" 开关,例如:file = open(".git/objects/09/72d7651ff85bedf464fba868c2ef434543916a", "rb") - Igor Popov
未知的压缩方法。 - cybernard

9

看起来Mark Adler考虑到了我们,他写了一个例子来展示如何实现这一点: http://www.zlib.net/zpipe.c

它只需要安装zlib头文件,就可以使用gcc -lz编译。我在处理git事务时将生成的二进制文件复制到了我的/usr/local/bin/zpipe中。


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