141

据我了解,当 Git 将 SHA1 哈希分配给文件时,此 SHA1 根据其内容对文件是唯一的。

因此,如果文件从一个存储库移动到另一个存储库,则该文件的 SHA1 保持不变,因为其内容没有更改。

Git 如何计算 SHA1 摘要?它是否对完整的未压缩文件内容执行此操作?

我想模拟在 Git 之外分配 SHA1。

4

13 回答 13

259

这就是 Git 计算文件(或者,用 Git 术语,“blob”)的 SHA1 的方式:

sha1("blob " + filesize + "\0" + data)

因此,您无需安装 Git 即可轻松自行计算。请注意,“\0”是 NULL 字节,而不是两个字符的字符串。

例如,一个空文件的哈希:

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()
于 2009-02-16T09:25:54.410 回答
18

一点好东西:在外壳中

echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum
于 2010-02-24T11:32:15.817 回答
9

如果您没有安装 git,您可以创建一个 bash shell 函数来轻松计算它。

git_id () { printf 'blob %s\0' "$(ls -l "$1" | awk '{print $5;}')" | cat - "$1" | sha1sum | awk '{print $1}'; }
于 2009-07-31T15:33:51.977 回答
4

查看git-hash-object的手册页。您可以使用它来计算任何特定文件的 git 哈希。我认为git 不仅仅是将文件的内容提供给哈希算法,但我不确定,如果它确实提供了额外的数据,我不知道它是什么。

于 2009-02-16T09:24:43.443 回答
2
/// 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# 中的解决方案。

于 2010-02-23T17:29:12.467 回答
2

完整的 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() 
于 2013-09-20T21:03:22.457 回答
1

在 Perl 中(另请参见http://search.cpan.org/dist/Git-PurePerl/上的 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();
于 2010-04-01T01:04:38.773 回答
1

在 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
于 2011-08-06T09:29:21.953 回答
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
于 2014-06-27T18:49:06.530 回答
1

一个小 Bash 脚本应该产生与以下内容相同的输出git hash-object

#!/bin/sh
( 
    echo -en 'blob '"$(stat -c%s "$1")"'\0';
    cat "$1" 
) | sha1sum | cut -d\  -f 1
于 2016-09-06T15:12:02.727 回答
1

您也可以在文件上应用相同的

$ echo "foobar" > foo.txt
$ echo "$(cat foo.txt)"|(read f; echo -en "blob "$((${#f}+1))"\0$f\n" )|openssl sha1
323fae03f4606ea9991df8befbb2fca795e648fa
于 2021-06-17T13:10:54.440 回答
0

在 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}`)
}
于 2018-06-28T08:57:50.770 回答
-4

有趣的是,Git 显然会在数据的末尾添加一个换行符,然后才会对其进行哈希处理。一个只包含“Hello World!”的文件 得到一个 980a0d5... 的 blob 散列,与这个相同:

$ php -r 'echo sha1("blob 13" . chr(0) . "Hello World!\n") , PHP_EOL;'
于 2013-10-12T17:57:35.560 回答