【发布时间】:2010-10-07 20:22:49
【问题描述】:
据我了解,当 Git 将 SHA1 哈希分配给文件时,此 SHA1 根据其内容对文件是唯一的。
因此,如果文件从一个存储库移动到另一个存储库,则该文件的 SHA1 保持不变,因为其内容没有更改。
Git 如何计算 SHA1 摘要?它是否对完整的未压缩文件内容执行此操作?
我想模拟在 Git 之外分配 SHA1。
【问题讨论】:
据我了解,当 Git 将 SHA1 哈希分配给文件时,此 SHA1 根据其内容对文件是唯一的。
因此,如果文件从一个存储库移动到另一个存储库,则该文件的 SHA1 保持不变,因为其内容没有更改。
Git 如何计算 SHA1 摘要?它是否对完整的未压缩文件内容执行此操作?
我想模拟在 Git 之外分配 SHA1。
【问题讨论】:
您也可以在文件上应用相同的内容
$ echo "foobar" > foo.txt
$ echo "$(cat foo.txt)"|(read f; echo -en "blob "$((${#f}+1))"\0$f\n" )|openssl sha1
323fae03f4606ea9991df8befbb2fca795e648fa
【讨论】:
完整的 Python3 实现:
import os
from hashlib import sha1
def hashfile(filepath):
filesize_bytes = os.path.getsize(filepath)
s = sha1()
s.update(b"blob %u\0" % filesize_bytes)
with open(filepath, 'rb') as f:
s.update(f.read())
return s.hexdigest()
【讨论】:
在 JavaScript 中
const crypto = require('crypto')
const bytes = require('utf8-bytes')
function sha1(data) {
const shasum = crypto.createHash('sha1')
shasum.update(data)
return shasum.digest('hex')
}
function shaGit(data) {
const total_bytes = bytes(data).length
return sha1(`blob ${total_bytes}\0${data}`)
}
【讨论】:
应该产生与git hash-object 相同的输出的小 Bash 脚本:
#!/bin/sh
(
echo -en 'blob '"$(stat -c%s "$1")"'\0';
cat "$1"
) | sha1sum | cut -d\ -f 1
【讨论】:
使用 Ruby,您可以执行以下操作:
require 'digest/sha1'
def git_hash(file)
data = File.read(file)
size = data.bytesize.to_s
Digest::SHA1.hexdigest('blob ' + size + "\0" + data)
end
【讨论】:
小礼物:在 shell 中
echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum
【讨论】:
echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum 与git hash-object path-to-file 的输出进行比较,它们会产生不同的结果。但是,echo -e ... 会产生正确的结果,除了有一个尾随 - (git hash-object 产生 no 尾随字符)。这是我应该担心的事情吗?
sha1sum 使用尾随 -,如果它从标准输入而不是从文件计算哈希。没什么可担心的。虽然关于-n 的奇怪之处在于,它应该抑制通常由回声附加的换行符。您的文件是否有一个空的最后一行,您忘记将其添加到您的 CONTENTS 变量中?
cat file | sha1sum 而不是sha1sum file,您将获得相同的输出(尽管有更多的流程和管道)
这是 Git 计算文件(或者,用 Git 术语,“blob”)的 SHA1 的方式:
sha1("blob " + filesize + "\0" + data)
因此,您无需安装 Git,即可轻松自行计算。请注意,“\0”是 NULL 字节,而不是两个字符的字符串。
例如空文件的hash:
sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
另一个例子:
sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa"
$ echo "foobar" > foo.txt
$ git hash-object foo.txt
323fae03f4606ea9991df8befbb2fca795e648fa
这是一个 Python 实现:
from hashlib import sha1
def githash(data):
s = sha1()
s.update("blob %u\0" % len(data))
s.update(data)
return s.hexdigest()
【讨论】:
s.update() 上得到一个 TypeError: Unicode-objects must be encoded before hashing 异常。
s.update(("blob %u\0" % filesize).encode('utf-8')) 以避免TypeError。
有趣的是,很明显 Git 在数据被散列之前会在数据末尾添加一个换行符。一个只包含“Hello World!”的文件得到一个 980a0d5... 的 blob 散列,与这个相同:
$ php -r 'echo sha1("blob 13" . chr(0) . "Hello World!\n") , PHP_EOL;'
【讨论】:
git hash-object 添加的。请注意,使用echo "Hello World!" | git hash-object --stdin 会得到980a0d5...,而使用echo -n 会得到c57eff5... 的哈希值。
如果你没有安装 git,你可以创建一个 bash shell 函数来很容易地计算它。
git_id () { printf 'blob %s\0' "$(ls -l "$1" | awk '{print $5;}')" | cat - "$1" | sha1sum | awk '{print $1}'; }
【讨论】:
(stat --printf="blob %s\0" "$1"; cat "$1") | sha1sum -b | cut -d" " -f1.
在 Perl 中(另见 Git::PurePerl http://search.cpan.org/dist/Git-PurePerl/ )
use strict;
use warnings;
use Digest::SHA1;
my @input = <>;
my $content = join("", @input);
my $git_blob = 'blob' . ' ' . length($content) . "\0" . $content;
my $sha1 = Digest::SHA1->new();
$sha1->add($git_blob);
print $sha1->hexdigest();
【讨论】:
/// Calculates the SHA1 for a given string
let calcSHA1 (text:string) =
text
|> System.Text.Encoding.ASCII.GetBytes
|> (new System.Security.Cryptography.SHA1CryptoServiceProvider()).ComputeHash
|> Array.fold (fun acc e ->
let t = System.Convert.ToString(e, 16)
if t.Length = 1 then acc + "0" + t else acc + t)
""
/// Calculates the SHA1 like git
let calcGitSHA1 (text:string) =
let s = text.Replace("\r\n","\n")
sprintf "blob %d%c%s" (s.Length) (char 0) s
|> calcSHA1
这是 F# 中的解决方案。
【讨论】:
在 Perl 中:
#!/usr/bin/env perl
use Digest::SHA1;
my $content = do { local $/ = undef; <> };
print Digest::SHA1->new->add('blob '.length($content)."\0".$content)->hexdigest(), "\n";
作为 shell 命令:
perl -MDigest::SHA1 -E '$/=undef;$_=<>;say Digest::SHA1->new->add("blob ".length()."\0".$_)->hexdigest' < file
【讨论】:
查看git-hash-object 的手册页。您可以使用它来计算任何特定文件的 git 哈希。我认为 git 不仅仅将文件的内容提供给哈希算法,但我不确定,如果它确实提供了额外的数据,我不知道它是什么是。
【讨论】: