Git SHA1'leri Git olmayan bir dosyaya nasıl atayabilirim?


138

Git, bir dosyaya bir SHA1 karması atadığında anladığım kadarıyla, bu SHA1 içeriğine göre benzersizdir.

Sonuç olarak, bir dosya bir havuzdan diğerine taşınırsa, dosya için SHA1 içeriği değişmeden aynı kalır.

Git SHA1 özetini nasıl hesaplar? Sıkıştırılmamış dosya içeriğinin tamamını kullanıyor mu?

Git dışında SHA1'leri atamayı taklit etmek istiyorum.




Yanıtlar:


255

Git, bir dosya için SHA1'i şu şekilde hesaplar (veya Git terimleriyle bir "damla"):

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

Böylece Git yüklü olmadan kolayca kendiniz hesaplayabilirsiniz. "\ 0" ın iki karakterli bir dize değil, NULL bayt olduğunu unutmayın.

Örneğin, boş bir dosyanın karması:

sha1("blob 0\0") = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"

$ touch empty
$ git hash-object empty
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

Başka bir örnek:

sha1("blob 7\0foobar\n") = "323fae03f4606ea9991df8befbb2fca795e648fa"

$ echo "foobar" > foo.txt
$ git hash-object foo.txt 
323fae03f4606ea9991df8befbb2fca795e648fa

İşte bir Python uygulaması:

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

Bu cevap Python 2'yi mi varsayıyor? Bunu Python 3'te denediğimde TypeError: Unicode-objects must be encoded before hashingilk s.update()satırda bir istisna alıyorum .
Mark Booth

3
Python 3 ile verileri kodlamanız gerekir: s.update(("blob %u\0" % filesize).encode('utf-8'))önlemek için TypeError.
Mark Booth

Utf-8 olarak kodlama işe yarar, ancak ilk etapta sadece bir bayt dizesinden oluşturmak daha iyidir (utf-8 kodlaması çalışır çünkü unicode karakterlerin hiçbiri ASCII değildir).
torek

Bahsetmeye değer bir diğer şey de git hash-object'in "\ r \ n" yerine "\ n" veri içeriğinin yerini almasıdır. Çok iyi "\" şeritler olabilir, ben kontrol etmedi.
user420667

1
Buraya bir dosya ve ağaç hash üreteci Python 2 + 3 (her ikisi de bir arada) uygulamasını koydum : github.com/chris3torek/scripts/blob/master/githash.py (ağaç hasher bir dizin ağacı okur).
torek

17

Küçük bir goodie: kabukta

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

1
echo -en "blob ${#CONTENTS}\0$CONTENTS" | sha1sumÇıktılarını karşılaştırıyorum git hash-object path-to-fileve farklı sonuçlar üretiyorlar. Ancak, echo -e ...bir iz - ( arka karakter içermez ) dışında doğru sonuçları git hash-objectüretir . Bu endişelenmem gereken bir şey mi?
SinirliWithFormsDesigner

2
@FrustratedWithFormsDesigner: Sondaki karmayı bir dosyadan değil stdin'den hesaplamışsa -kullanılır sha1sum. Endişelenecek birşey yok. Garip bir şey olsa da -n, bu normalde yankı tarafından eklenen satırsonu bastırmalıdır. Dosyanızda CONTENTSdeğişkeninize eklemeyi unuttuğunuz boş bir son satır var mı?
triko

Evet, haklısın. Ve sha1sum'un çıktısının sadece karma olması gerektiğini düşünmüştüm , ancak sed veya başka bir şeyle kaldırmak zor değil.
Hayal kırıklığına

@FrustratedWithFormsDesigner: (Daha fazla işlem ve borular) cat file | sha1sumyerine kullanırsanız aynı çıktıyı alırsınızsha1sum file
knittl

8

Git yüklü değilse, bash kabuk işlevini kolayca hesaplayabilirsiniz.

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

1
Daha kısa bit: (stat --printf="blob %s\0" "$1"; cat "$1") | sha1sum -b | cut -d" " -f1.
sschuberth

4

Git-hash-object için man sayfasına bir göz atın . Herhangi bir dosyanın git karmasını hesaplamak için kullanabilirsiniz. Ben düşünüyorum o git beslemeleri daha karma algoritması içine dosyanın sadece içeriğine değil, ama emin bilmiyorum ve ekstra veri besleme eğer, bunun ne olduğunu bilmiyorum.


2
/// 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

Bu F # 'da bir çözümdür.


Hala umlauts ile ilgili sorunlar var: calcGitSHA1 ("ü"). Git karma nesnesi imlemleri nasıl ele alır?
forki23

bloğu bir bytestream olarak ele almalıdır, bu da ü muhtemelen 2 uzunluğuna (unicode) sahip olduğu anlamına gelir, F♯'nun Length özelliği 1 uzunluğunu döndürür (çünkü sadece bir görünür karakter)
knittl

Ancak System.Text.Encoding.ASCII.GetBytes ("ü"), 1 öğeli bir bayt dizisi döndürür.
forki23

UTF8 ve 2'yi dize uzunluğu olarak kullanmak bir bayt dizisi verir: [98; 108; 111; 98; 32; 50; 0; 195; 188] ve bunun için 99fe40df261f7d4afd1391fe2739b2c7466fe968 bir SHA1. Bu da git SHA1 değil.
forki23

1
Karakter dizelerine hiçbir zaman özet uygulamamalısınız. Bunun yerine, karakter dizesini açık bir kodlama kullanarak baytlara dönüştürerek elde edebileceğiniz bayt dizelerine (bayt dizileri) uygulamanız gerekir.
dolmen

2

Tam Python3 uygulaması:

import os
from hashlib import sha1

def hashfile(filepath):
    filesize_bytes = os.path.getsize(filepath)

    s = sha1()
    s.update(b"blob %u\0" % filesize_bytes)

    with open(filepath, 'rb') as f:
        s.update(f.read())

    return s.hexdigest() 

2
Gerçekten istediğiniz şey ASCII kodlamasıdır. UTF8 yalnızca ASCII ve "blob x \ 0" ile uyumlu olduğu için burada çalışır, yalnızca <= 127 kodlu karakterler içerir.
Ferdinand Beyer

1

Perl dilinde:

#!/usr/bin/env perl
use Digest::SHA1;

my $content = do { local $/ = undef; <> };
print Digest::SHA1->new->add('blob '.length($content)."\0".$content)->hexdigest(), "\n";

Bir kabuk komutu olarak:

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

1

Ve Perl'de (ayrıca bkz. Git :: PurePerl http://search.cpan.org/dist/Git-PurePerl/ )

use strict;
use warnings;
use Digest::SHA1;

my @input = &lt;&gt;;

my $content = join("", @input);

my $git_blob = 'blob' . ' ' . length($content) . "\0" . $content;

my $sha1 = Digest::SHA1->new();

$sha1->add($git_blob);

print $sha1->hexdigest();

1

Ruby'yi kullanarak böyle bir şey yapabilirsiniz:

require 'digest/sha1'

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

1

Aynı çıktıyı üretmesi gereken küçük bir Bash betiği git hash-object:

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

0

JavaScript'te

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

-4

Açıkçası Git'in verilerin karmalaştırılmadan önce yeni satır karakteri eklediğine dikkat çekmek gerekir. "Merhaba Dünya!" Dan başka bir şey içermeyen dosya 980a0d5 ... bir blob hash alır, ki bununla aynı:

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

4
Bu satırsonu metin düzenleyiciniz tarafından ekleniyor, değil git hash-object. Yapıyor unutmayın echo "Hello World!" | git hash-object --stdinverir 980a0d5...kullanırken, echo -nbir karma verir c57eff5...yerine.
bdesham
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.