Si je comprends bien, lorsque Git attribue un hachage SHA1 à un fichier, ce SHA1 est unique pour le fichier en fonction de son contenu.
En conséquence, si un fichier passe d'un référentiel à un autre, SHA1 reste le même car son contenu n'a pas changé.
Comment Git calcule-t-il le résumé SHA1? Le fait-il sur le contenu complet du fichier non compressé?
Je voudrais émuler l'attribution de SHA1 en dehors de Git.
Voici comment Git calcule le SHA1 pour un fichier (ou, en termes git, un "blob"):
sha1("blob " + filesize + "\0" + data)
Ainsi, vous pouvez facilement le calculer vous-même sans avoir installé Git. Notez que "\ 0" est l'octet NULL et non une chaîne de deux caractères.
Par exemple, le hachage d'un fichier vide:
sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
Un autre exemple:
sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa"
$ echo "foobar" > foo.txt
$ git hash-object foo.txt
323fae03f4606ea9991df8befbb2fca795e648fa
Voici une implémentation Python:
from hashlib import sha1
def githash(data):
s = sha1()
s.update("blob %u\0" % len(data))
s.update(data)
return s.hexdigest()
Un petit goodie: dans Shell
echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum
Vous pouvez créer une fonction shell bash pour la calculer assez facilement si vous n’avez pas installé git.
git_id () { printf 'blob %s\0' "$(ls -l "$1" | awk '{print $5;}')" | cat - "$1" | sha1sum | awk '{print $1}'; }
Jetez un coup d’œil à la page de manuel relative à git-hash-object . Vous pouvez l'utiliser pour calculer le hachage git d'un fichier particulier. Je pense que git alimente plus que le contenu du fichier dans l'algorithme de hachage, mais je ne le sais pas avec certitude, et s'il contient des données supplémentaires, je ne sais pas ce que c'est.
Implémentation complète de 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
Ceci est une solution en F #.
Et en Perl (voir aussi Git :: PurePerl sur 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();
Un petit script Bash qui devrait produire une sortie identique à git hash-object
:
#!/bin/sh
(
echo -en 'blob '"$(stat -c%s "$1")"'\0';
cat "$1"
) | sha1sum | cut -d\ -f 1
En 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";
En tant que commande Shell:
Perl -MDigest::SHA1 -E '$/=undef;$_=<>;say Digest::SHA1->new->add("blob ".length()."\0".$_)->hexdigest' < file
En utilisant Ruby, vous pourriez faire quelque chose comme ceci:
require 'digest/sha1'
def git_hash(file)
data = File.read(file)
size = data.bytesize.to_s
Digest::SHA1.hexdigest('blob ' + size + "\0" + data)
end
En 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}`)
}