Как назначить ЖКТ и SHA1 для файла без ГИТ?


Как я понимаю, когда Git назначает хэш SHA1 файлу, этот SHA1 уникален для файла на основе его содержимого.

в результате, если файл перемещается из одного репозитория в другой SHA1 для файла остается таким же, как и ее содержание не изменились.

как Git вычисляет дайджест SHA1? Делает ли он это на полном несжатом содержимом файла?

Я хотел бы эмулировать назначение SHA1 за пределами Git.

12 136

12 ответов:

вот как Git вычисляет SHA1 для файла (или, в терминах Git, "blob"):

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

таким образом, вы можете легко вычислить его самостоятельно без установки Git. Обратите внимание, что" \0 " -это нулевой байт, а не двухсимвольная строка.

например, хэш пустого файла:

sha1("blob 0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"

$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

еще пример:

sha1("blob 7foobar\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" % len(data))
    s.update(data)
    return s.hexdigest()

немного goodie: в оболочке

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

вы можете сделать функцию оболочки bash, чтобы вычислить ее довольно легко, если у вас не установлен git.

git_id () { printf 'blob %s' "$(ls -l "" | awk '{print ;}')" | cat - "" | sha1sum | awk '{print }'; }

взгляните на man-странице git-hash-object. Вы можете использовать его для вычисления git-хэша любого конкретного файла. Я думаю что git подает больше, чем просто содержимое файла в хэш-алгоритм, но я не знаю наверняка, и если он подает дополнительные данные, я не знаю, что это такое.

полная реализация Python3:

import os
from hashlib import sha1

def hashfile(filepath):
    filesize_bytes = os.path.getsize(filepath)

    s = sha1()
    s.update(("blob %u" % filesize_bytes).encode('utf-8'))

    with open(filepath, 'rb') as f:
        s.update(f.read())

    return s.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)."".$content)->hexdigest(), "\n";

в командную строку:

perl -MDigest::SHA1 -E '$/=undef;$_=<>;say Digest::SHA1->new->add("blob ".length()."".$_)->hexdigest' < file

и в Perl (см. Также Git:: PurePerl at http://search.cpan.org/dist/Git-PurePerl/ )

use strict;
use warnings;
use Digest::SHA1;

my @input = &lt;&gt;;

my $content = join("", @input);

my $git_blob = 'blob' . ' ' . length($content) . "" . $content;

my $sha1 = Digest::SHA1->new();

$sha1->add($git_blob);

print $sha1->hexdigest();

используя Ruby, вы можете сделать что-то вроде этого:

require 'digest/sha1'

def git_hash(file)
  data = File.read(file)
  size = data.bytesize.to_s
  Digest::SHA1.hexdigest('blob ' + size + "" + data)
end

немного bash скрипт, который должен производить идентичный вывод на git hash-object:

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

В 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}${data}`)
}

интересно отметить, что, очевидно, git добавляет символ новой строки в конец данных, прежде чем он будет хешироваться. Файл, не содержащий ничего, кроме " Hello World!"получает blob-хэш 980a0d5..., который так же, как и этот:

$ php -r 'echo sha1("blob 13" . chr(0) . "Hello World!\n") , PHP_EOL;'