Como atribuir um Git SHA1 para um arquivo sem o Git?

Pelo que entendi, quando o Git atribui um hash SHA1 a um arquivo, este SHA1 é exclusivo do arquivo com base em seu conteúdo.

Como resultado, se um arquivo for movido de um repository para outro, o SHA1 do arquivo permanecerá igual ao seu conteúdo, que não foi alterado.

Como o Git calcula o resumo SHA1? Isso é feito no conteúdo completo do arquivo não compactado?

Eu gostaria de emular a atribuição de SHA1 fora do Git.

É assim que o Git calcula o SHA1 para um arquivo (ou, em termos de Git, um “blob”):

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

Assim, você pode facilmente calculá-lo sem ter o Git instalado. Observe que “\ 0” é o byte NULL, não uma cadeia de dois caracteres.

Por exemplo, o hash de um arquivo vazio:

 sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" $ touch empty $ git hash-object empty e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 

Outro exemplo:

 sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa" $ echo "foobar" > foo.txt $ git hash-object foo.txt 323fae03f4606ea9991df8befbb2fca795e648fa 

Aqui está uma implementação do Python:

 from hashlib import sha1 def githash(data): s = sha1() s.update("blob %u\0" % len(data)) s.update(data) return s.hexdigest() 

Um pouco de guloseima: com casca

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

Você pode fazer uma function bash shell para calcular facilmente se você não tiver o git instalado.

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

Dê uma olhada na página man do git-hash-object . Você pode usá-lo para calcular o hash git de qualquer arquivo em particular. Eu acho que o git alimenta mais do que apenas o conteúdo do arquivo no algoritmo hash, mas eu não sei ao certo, e se ele alimenta dados extras, eu não sei o que é.

Implementação completa do Python3:

 import os from hashlib import sha1 def hashfile(filepath): filesize_bytes = os.path.getsize(filepath) s = sha1() s.update(("blob %u\0" % 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 

Esta é uma solução em F #.

Em 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"; 

Como um comando shell:

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

E em Perl (veja também Git :: PurePerl em 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(); 

Usando Ruby, você poderia fazer algo assim:

 require 'digest/sha1' def git_hash(file) data = File.read(file) size = data.bytesize.to_s Digest::SHA1.hexdigest('blob ' + size + "\0" + data) end 

Um pequeno script Bash que deve produzir uma saída idêntica ao git hash-object :

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

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

É interessante notar que, obviamente, o Git adiciona um caractere de nova linha ao final dos dados antes que ele seja dividido em hash. Um arquivo contendo nada além de “Hello World!” Obtém um hash de blob de 980a0d5 …, o mesmo que este:

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