Как назначить ЖКТ и SHA1 для файла без ГИТ?
Как я понимаю, когда Git назначает хэш SHA1 файлу, этот SHA1 уникален для файла на основе его содержимого.
в результате, если файл перемещается из одного репозитория в другой SHA1 для файла остается таким же, как и ее содержание не изменились.
как Git вычисляет дайджест SHA1? Делает ли он это на полном несжатом содержимом файла?
Я хотел бы эмулировать назначение SHA1 за пределами Git.
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()
вы можете сделать функцию оболочки 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 = <>; 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}`) }