在特定提交中获取文件的SHA哈希值

5

我怎样能够在指定的提交中获得一个文件的SHA哈希? 我可以使用 git log file 获取所有涉及该文件的提交,但是怎样能够在每个特定的提交中获取该文件的SHA哈希值呢?

我认为我可以通过检出提交,然后使用 git-hash-object 来完成,但一定有更简单的方法。


你需要文件内容的逐字SHA1,还是想要用于存储Git历史记录中文件的blob对象的SHA1? - knittl
我理解的是否正确,blob对象包括文件模式和其他内容?如果是这样的区别,我只需要文件内容,就像运行sha1sum file一样。 - graywolf
该 Blob 包含附加的类型和长度信息以及一些空字节。 - knittl
3个回答

7

在某个提交中获取文件的Git哈希值有一种非常快速的方法:

git rev-parse <commit-ID>:/path/to/file

Git的哈希值是由单词blob和一个空格组成的SHA-1,后面跟着一个十进制ASCII字符串,该字符串表示文件的大小(以字节为单位),然后是一个NUL字节,最后是文件的内容:

size=$(wc -c $file)
(printf "blob %d\0" $size; cat $file) | sha1sum -

从评论中看来,你似乎想要文件内容的实际SHA-1值(就像其他人从提取文件并在其上运行sha1sum得到的值),而不是git哈希:

git show <commit-ID>:path | sha1sum -

这是一种通用的方法(不仅适用于Bash),可以使用bash的<(,只需确保您已安装fdesc文件系统即可。


提交 ID 也可以是分支名称、哈希值或其他命名方式,如 git help revisions 中所述。您可以使用相同的命名方法对文件进行差异比较(适用于查看分叉之间的偏差)。 - Philip Oakley

6
git showgit log 是亲戚,它们共享选项。您的问题询问了与特定提交关联的文件的 SHA-1 对象名称,但是还询问了沿着历史记录每个提交的相同信息。 --raw 选项可以提供您需要的信息。以下示例将使用 git 的自有存储库。
要显示更改了哪些文件,请使用 git showgit log -1 命令。后者仅对标记的提交生成输出,而不会为标记对象生成输出。
$ git log -1 --raw v2.8.1
commit d95553a6b8c5153f541adcfc3346004e8249b0e6
Author: Junio C Hamano <gitster@pobox.com>
Date:   Sun Apr 3 10:11:35 2016 -0700
Git 2.8.1
Signed-off-by: Junio C Hamano <gitster@pobox.com>
:000000 100644 0000000... ef6d80b... A Documentation/RelNotes/2.8.1.txt :100644 100644 adc940b... 8afe349... M Documentation/git.txt :100755 100755 4e9450b... 46595da... M GIT-VERSION-GEN :120000 120000 7db3040... d40c3e1... M RelNotes
每个更改行包含
  • 开始或源模式(000000 表示已创建或未合并)
  • 结果或目标模式(000000 表示已删除或未合并)
  • 源 SHA-1(创建时为所有零)
  • 目标 SHA-1(删除时为所有零)
  • 状态代码加可选数字分数(以上 A 表示添加,M 表示修改)
  • 路径
有关完整详细信息,请参见 git diff 的文档中的“原始输出格式”
与 v2.8.1 标记关联的文件 RelNotes 的 SHA-1 对象名称是 d40c3e1,我们可以使用以下命令验证并展开到所有 40 个数字:
$ git rev-parse v2.8.1:RelNotes
d40c3e126c03b0e4bd9c6162f63a35a45f5e9020
要显示指向给定版本下对应于 Documentation/RelNotes 的符号链接 RelNotes 在版本 2.8.1 的历史记录中沿途的哈希值:
$ git log --raw v2.8.1 -- RelNotes
提交记录 d95553a6b8c5153f541adcfc3346004e8249b0e6
作者: Junio C Hamano <gitster@pobox.com>
日期:   2016年4月3日 下午10:11:35
Git 2.8.1
签名: Junio C Hamano <gitster@pobox.com>
:120000 120000 7db3040... d40c3e1... M RelNotes
提交记录 c9906e47c065940bfe1a9992da494a8f437a49ac 作者: Junio C Hamano <gitster@pobox.com> 日期: 2016年1月13日 上午12:20:51
First batch for post 2.7 cycle 签名: Junio C Hamano <gitster@pobox.com>
:120000 120000 3ba13ce... 7db3040... M RelNotes
提交记录 24a00ef646974be49ef7138239c3803805400797 作者: Junio C Hamano <gitster@pobox.com> 日期: 2015年10月6日 上午7:58:10
Start cycle toward 2.7
签名: Junio C Hamano <gitster@pobox.com>
:120000 120000 def6ebd... 3ba13ce... M RelNotes [...]

使用--abbrev选项以获取哈希值的所有40个十六进制数字。这里的输出看起来比较冗长,因为git show命令的输出涵盖了v2.8.1标签和指向其所指向的提交。

$ git show --raw --abbrev=40 v2.8.1
标签 v2.8.1
标记人: Junio C Hamano <gitster@pobox.com>
日期:   2016年4月3日 星期日 -0700
Git 2.8.1 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1
iQIcBAABAgAGBQJXAU94AAoJELC16IaWr+bLopQQAONTo52BGPCr7exw757SKY90 gYsHDxTaNpPtGZS7ltdOiEESPG3Mx3w1OYk7CBPtxjBLM+JvEdcZsCKrs/RlTrKL lTc53WHC1tUa8EYjEyHNq4z0E2y4tCTNsj5eD2n/lAdTn2SK59bL4DEouDP2mYJU 3pUkujD9tu/ATw1s77VNiHxcrg9V9TdltaP2+lkHPzXXx8fb8kkabFRkzqvQdgfe Qe0mZEHKRZY4nEO16dKukalxyWW0iMfoSVeRTjJiQU4HEcMyEnG3lfKeI1ddKVTQ +XfAM6QianXqdfHRt5ol9MwCm9HAcGWu82caIBOTsc3L7bDrbJTTkDOvwpmVUDJi WcqgocDGr/x7RA0/E8bqoIv40UXx07DzBTv3mKBo2CMvkow6pgQjsKKfPrvoNKyC qFqp07A3UXgLWeWLF2iaYJklkq2jEeLPKOCJ1lJcPUg+Kk20+FQEo1XPERnrosoz xHDDMBy7Vnvd0ij8Ipaxj2XHfIVYHC/WcrfsjiRYa1sHMjdTw/6I0tdtdUkDiY2W 70AsYQUWPtU52tSuK7divMoym3g583bNtu5X+6STDtLZc5XbVAtMEg5PYadTuwci tTmXTUrti2qLsDp2XZI7rKbKVo5JyW8BYC8BeLUwgVnkj9svG5+6rlTKtgXa+hCo L9gDU1Iie03IlIHnL+/s =NLvn -----END PGP SIGNATURE-----
提交 d95553a6b8c5153f541adcfc3346004e8249b0e6 作者: Junio C Hamano <gitster@pobox.com> 日期: 2016年4月3日 星期日 -0700
Git 2.8.1
签名: Junio C Hamano <gitster@pobox.com>
:000000 100644 0000000000000000000000000000000000000000 ef6d80b008a0a7970238404b034593be27e933c3 A Documentation/RelNotes/2.8.1.txt :100644 100644 adc940bf7591069c74c9b47aa5e5686e0438d606 8afe349781d57527083fdb75511959fd25a4239b M Documentation/git.txt :100755 100755 4e9450b3ae0c403820f0166435c52c4ea74e7451 46595dad2234f861198347ef8f4f60d167061709 M GIT-VERSION-GEN :120000 120000 7db30403c3471e15f4f15a5e68016d7926b3e3de d40c3e126c03b0e4bd9c6162f63a35a45f5e9020 M RelNotes
一个blob(git代表文件内容的方式)的SHA-1对象名称与在文件上运行sha1sum的结果不相同,因为git在前面添加了元数据:文本字面量“blob”,后跟一个空格,再后跟内容长度的十进制表示,并以NUL字节结尾。要计算向后依次版本中文件内容的SHA-1散列,请使用以下命令:

$ for commit in $(git log --pretty=%H v2.8.1 -- RelNotes | head -3) ; \ do git show ${commit}:RelNotes | sha1sum ; \ done ce5501f9daadf110a20a4e4eccdfed63ef4b27e3 - bd4d920214c4a48d8820292e24f020690595858d - 5d47b511d86abd490fa4f2c2a8d4ef3589e1aecf -

通过使用--pretty =%H和-- RelNotes参数,我们告诉git我们只需要触及RelNotes的提交的SHA-1哈希值(限制为head -3的三个最近的)。然后对于这些提交中的每一个,我们将被跟踪的内容输入到sha1sum中。

如果您更喜欢使用xargs,则如下所示:

$ git log --pretty=%H v2.8.1 -- RelNotes | head -3 | xargs -I {} sh -c 'git show {}:RelNotes | sha1sum'


1

至少,您不需要检查提交记录。 git show 可以直接显示对象,包括 blob。 您可以将其发送到 git hash-object 而无需检出。

我认为应该有更有效的方法,但您可以这样做

git hash-object <(git show [commit]:[path])

所以,例如,
$ git hash-object <(git show master:Makefile)
3fb4e1cbe0019c691a504e3419ece252db6f60ab

提交的树已经有一个指向 blob 对象(文件)的指针。根据操作人员的要求,这可能已经足够了。 - knittl
太棒了,非常感谢。而且速度还相当快,对于项目中更改文件的整个历史记录在3秒以内完成。这将对我的一些脚本编写非常有帮助 :) 再次感谢。 - graywolf
@zebediah49:不过我有一个问题,为什么它显示的字符串与“sha1sum file”的输出不同?我的工作树没有任何更改,所以我希望第一个哈希与“sha1sum file”的输出相同。我正在使用这个命令:for commit in $(git log --oneline path/to/file | cut -d\ -f1); do git hash-object <(git show $commit:path/to/file); done - graywolf
我可以在不使用git-show部分的情况下复制该行为:git hash-object <file>sha1sum <file>产生不同的结果。 可能,它们正在做不同的事情--例如,git命令表示它“计算对象ID”,这显然不仅仅是其内容的SHA1摘要。 E:https://dev59.com/questions/FG435IYBdhLWcg3wqB4x - zebediah49
我想我可以直接使用sha1sum - <(git show $commit:path/to/file)吗? - graywolf
你已经将两种输入管道的方式结合起来了——sha1sum -正在要求从stdin中输入,因此您可以使用git show [thing] | sha1sum -,或者您可以使用匿名管道来假装给它一个文件sha1sum <(git show [thing])。在这种情况下,sha1sum看到您告诉它运行像/dev/fd/XX这样的文件,这是由shell创建的管道,其另一端连接到git show命令。同时使用两者是告诉sha1sum先哈希标准输入,然后再哈希这个命名管道。 - zebediah49

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