Come assegnare un Git SHA1 a un file senza Git?

Da quanto ho capito quando Git assegna un hash SHA1 a un file questo SHA1 è univoco per il file in base al suo contenuto.

Di conseguenza, se un file si sposta da un repository all’altro, SHA1 per il file rimane uguale a quello in cui i suoi contenuti non sono stati modificati.

In che modo Git calcola il digest SHA1? Lo fa sul contenuto del file non compresso completo?

Mi piacerebbe emulare l’assegnazione di SHA1 al di fuori di Git.

Ecco come Git calcola SHA1 per un file (o, in termini Git, un “blob”):

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

Così puoi facilmente calcolarlo da solo senza aver installato Git. Nota che “\ 0” è il byte NULL, non una stringa di due caratteri.

Ad esempio, l’hash di un file vuoto:

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

Un altro esempio:

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

Ecco un’implementazione Python:

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

Un piccolo tesoro: nella shell

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

Puoi fare una funzione di shell bash per calcolarlo abbastanza facilmente se non hai installato git.

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

Dai un’occhiata alla pagina man di git-hash-object . Puoi usarlo per calcolare l’hash git di qualsiasi file specifico. Penso che git fornisca più del contenuto del file nell’algoritmo di hash, ma non lo so per certo, e se fornisce dati extra, non so cosa sia.

Implementazione completa di 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 

Questa è una soluzione in F #.

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

Come comando della shell:

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

E in Perl (vedi anche Git :: PurePerl su 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, potresti fare qualcosa del genere:

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

Un piccolo script di Bash che dovrebbe produrre un output identico git hash-object :

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

In 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 notare che ovviamente Git aggiunge un carattere di fine riga alla fine dei dati prima che venga sottoposto a hash. Un file che contiene nient’altro che “Hello World!” ottiene un hash blob di 980a0d5 …, che è lo stesso di questo:

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