So wie ich es verstehe, wenn Git einer Datei einen SHA1-Hash zuweist, ist dieser SHA1 für die Datei aufgrund ihres Inhalts eindeutig.
Wenn eine Datei von einem Repository in ein anderes verschoben wird, bleibt der SHA1 für die Datei derselbe, da der Inhalt nicht geändert wurde.
Wie berechnet Git den SHA1-Verdau? Macht es den vollen Inhalt der unkomprimierten Datei?
Ich möchte die Zuweisung von SHA1s außerhalb von Git nacheifern.
So berechnet Git den SHA1 für eine Datei (oder, in Git ausgedrückt, einen "Blob"):
sha1("blob " + filesize + "\0" + data)
Sie können es also problemlos selbst berechnen, ohne dass Git installiert ist. Beachten Sie, dass "\ 0" das NULL-Byte ist, keine aus zwei Zeichen bestehende Zeichenfolge.
Zum Beispiel der Hash einer leeren Datei:
sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
Ein anderes Beispiel:
sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa"
$ echo "foobar" > foo.txt
$ git hash-object foo.txt
323fae03f4606ea9991df8befbb2fca795e648fa
Hier ist eine Python-Implementierung:
from hashlib import sha1
def githash(data):
s = sha1()
s.update("blob %u\0" % len(data))
s.update(data)
return s.hexdigest()
Ein kleines Bonbon: in Shell
echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sum
Sie können eine bash-Shell-Funktion erstellen, um diese ganz einfach zu berechnen, wenn Sie git nicht installiert haben.
git_id () { printf 'blob %s\0' "$(ls -l "$1" | awk '{print $5;}')" | cat - "$1" | sha1sum | awk '{print $1}'; }
Schauen Sie sich die Manpage für git-hash-object an. Sie können es verwenden, um den Git-Hash einer bestimmten Datei zu berechnen. Ich denke , dass Git mehr als nur den Inhalt der Datei in den Hash-Algorithmus einspeist, aber ich weiß es nicht genau und ob es einspeist In zusätzlichen Daten weiß ich nicht, was es ist.
Vollständige Python3-Implementierung:
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
Dies ist eine Lösung in F #.
Und in Perl (siehe auch Git :: PurePerl unter 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();
Ein kleines Bash-Skript, das eine identische Ausgabe zu git hash-object
erzeugen sollte:
#!/bin/sh
(
echo -en 'blob '"$(stat -c%s "$1")"'\0';
cat "$1"
) | sha1sum | cut -d\ -f 1
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";
Als Shell-Befehl:
Perl -MDigest::SHA1 -E '$/=undef;$_=<>;say Digest::SHA1->new->add("blob ".length()."\0".$_)->hexdigest' < file
Mit Ruby können Sie so etwas tun:
require 'digest/sha1'
def git_hash(file)
data = File.read(file)
size = data.bytesize.to_s
Digest::SHA1.hexdigest('blob ' + size + "\0" + data)
end
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}`)
}