【问题标题】:How to assign a Git SHA1's to a file without Git?如何在没有 Git 的情况下将 Git SHA1 分配给文件?
【发布时间】:2010-10-07 20:22:49
【问题描述】:

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

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

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

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

【问题讨论】:

标签: git sha1


【解决方案1】:

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

$ echo "foobar" > foo.txt
$ echo "$(cat foo.txt)"|(read f; echo -en "blob "$((${#f}+1))"\0$f\n" )|openssl sha1
323fae03f4606ea9991df8befbb2fca795e648fa

【讨论】:

    【解决方案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() 
    

    【讨论】:

    • 您真正想要的是 ASCII 编码。 UTF8 仅适用于此处,因为它与 ASCII 兼容,并且“blob x\0”仅包含代码
    【解决方案3】:

    在 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}`)
    }
    

    【讨论】:

      【解决方案4】:

      应该产生与git hash-object 相同的输出的小 Bash 脚本:

      #!/bin/sh
      ( 
          echo -en 'blob '"$(stat -c%s "$1")"'\0';
          cat "$1" 
      ) | sha1sum | cut -d\  -f 1
      

      【讨论】:

        【解决方案5】:

        使用 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
        

        【讨论】:

          【解决方案6】:

          小礼物:在 shell 中

          echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum
          

          【讨论】:

          • 我将echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sumgit hash-object path-to-file 的输出进行比较,它们会产生不同的结果。但是,echo -e ... 会产生正确的结果,除了有一个尾随 -git hash-object 产生 no 尾随字符)。这是我应该担心的事情吗?
          • @FrustratedWithFormsDesigner:sha1sum 使用尾随 -,如果它从标准输入而不是从文件计算哈希。没什么可担心的。虽然关于-n 的奇怪之处在于,它应该抑制通常由回声附加的换行符。您的文件是否有一个空的最后一行,您忘记将其添加到您的 CONTENTS 变量中?
          • 是的,你是对的。而且我认为 sha1sum 的输出应该 only 是散列,但用 sed 或其他东西删除它并不难。
          • @FrustratedWithFormsDesigner:如果您使用cat file | sha1sum 而不是sha1sum file,您将获得相同的输出(尽管有更多的流程和管道)
          【解决方案7】:

          这是 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()
          

          【讨论】:

          • 这个答案是否假设 Python 2?当我在 Python 3 上尝试这个时,我在第一行 s.update() 上得到一个 TypeError: Unicode-objects must be encoded before hashing 异常。
          • 使用 python 3 您需要对数据进行编码:s.update(("blob %u\0" % filesize).encode('utf-8')) 以避免TypeError
          • 编码为 utf-8 将起作用,但首先从字节字符串构建它可能更好(utf-8 编码有效,因为没有一个 unicode 字符是非 ASCII)。
          • 另外值得一提的是,git hash-object 似乎也将数据内容中的“\r\n”替换为“\n”。它可能会完全去掉“\r”,我没有检查。
          • 我在这里放了一个文件树哈希生成器的Python 2 + 3(合二为一)实现:github.com/chris3torek/scripts/blob/master/githash.py(树哈希读取目录树) .
          【解决方案8】:

          有趣的是,很明显 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... 的哈希值。
          【解决方案9】:

          如果你没有安装 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.
          【解决方案10】:

          在 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();
          

          【讨论】:

            【解决方案11】:
            /// 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# 中的解决方案。

            【讨论】:

            • 我仍然有变音符号的问题: calcGitSHA1("ü").ShouldBeEqualTo("0f0f3e3b1ff2bc6722afc3e3812e6b782683896f") 但是我的函数给出了 0d758c9c7bc06c1e307f05d922d896aaf0c8a。任何想法 git hash-object 如何处理变音符号?
            • 它应该将 blob 作为字节流处理,这意味着 ü 的长度可能为 2(unicode),F♯ 的 Length 属性将返回长度 1(因为它只是一个可见字符)
            • 但是 System.Text.Encoding.ASCII.GetBytes("ü") 返回一个包含 1 个元素的字节数组。
            • 使用 UTF8 和 2 作为字符串长度给出一个字节数组:[98; 108; 111; 98; 32; 50; 0; 195; 188],因此 SHA1 为 99fe40df261f7d4afd1391fe2739b2c7466fe968。这也不是 git SHA1。
            • 您绝不能将摘要应用于字符串。相反,您必须将它们应用于字节字符串(字节数组),您可以通过使用显式编码将字符串转换为字节来获得这些字符串。
            【解决方案12】:

            在 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
            

            【讨论】:

              【解决方案13】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-08-08
                • 2010-11-19
                • 2012-08-20
                • 1970-01-01
                • 1970-01-01
                • 2012-06-09
                • 1970-01-01
                • 2017-08-01
                相关资源
                最近更新 更多