Git hesaplama dosyası nasıl karma oluşturur?


124

Ağaç nesnelerinde depolanan SHA1 hash değerleri (döndürülen git ls-tree) dosya içeriğinin SHA1 hash değerleri ile eşleşmiyor (döndürdüğü gibi sha1sum)

$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e  -

Git hesaplama dosyası nasıl karma oluşturur? Hash'i hesaplamadan önce içeriği sıkıştırıyor mu?



1
Daha fazla ayrıntı için ayrıca bkz. Progit.org/book/ch9-2.html
netvope

5
netvope'un bağlantısı artık yok gibi görünüyor. Sanırım yeni konum: git-scm.com/book/en/Git-Internals-Git-Objects , git-scm.com/book'dan §9.2
Rhubbarb

Yanıtlar:


122

Git, nesnenin önüne "blob", ardından uzunluk (insan tarafından okunabilir bir tam sayı olarak) ve ardından bir NUL karakteri ekler.

$ echo -e 'blob 14\0Hello, World!' | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d

Kaynak: http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html


2
Ayrıca "\ r \ n" yi "\ n" ile değiştirdiğini, ancak izole edilmiş "\ r "'leri yalnız bıraktığını belirtmekte fayda var.
user420667

8
^ yukarıdaki açıklamada düzeltme: bazen git, birinin eol / autocrlf ayarlarına bağlı olarak yukarıdaki değişikliği yapar.
user420667

5
Bunu ayrıca çıktısıyla da karşılaştırabilirsiniz echo 'Hello, World!' | git hash-object --stdin. İsteğe bağlı olarak, --no-filterscrlf dönüşümünün olmamasını belirtebilir veya git'in (ayrıca @ user420667) --path=somethi.ngaracılığıyla belirtilen filtreyi kullanmasına izin vermeyi belirtebilirsiniz gitattributes. Ve -waslında hiç damla göndermek için .git/objects(eğer varsa vardır bir git repo).
Tobias Kienzler

Eşdeğerliği ifade etmek, mantıklı olmak için: echo -e 'blob 16\0Hello, \r\nWorld!' | shasum == echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters ve aynı zamanda \nve 15 ile eşdeğer olacaktır .
Peter Krauss

1
echoçıktıya, git'e de aktarılan bir satırsonu ekler. Bu yüzden 14 karakter. Yankıyı satırsonu olmadan kullanmak için şunu yazınecho -n 'Hello, World!'
Bouke Versteegh

36

Sadece cevabı genişletiyorum @Leif Gruenwoldtve tarafından sağlanan referansta ne olduğunu detaylandırıyorum@Leif Gruenwoldt

Kendin Yap..

  • Adım 1. Deponuzda boş bir metin belgesi (adın önemi yoktur) oluşturun
  • Adım 2. Belgeyi Hazırlayın ve Teslim Edin
  • 3. Adım. Blob'un karmasını çalıştırarak tanımlayın git ls-tree HEAD
  • Adım 4. Blob'un hash değerini bulun e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • Adım 5. Sürprizinizden kurtulun ve aşağıyı okuyun

GIT, commit karmalarını nasıl hesaplar?

    Commit Hash (SHA1) = SHA1("blob " + <size_of_file> + "\0" + <contents_of_file>)

Metin blob⎵sabit bir önek ve \0aynı zamanda sabit ve NULLkarakterdir. <size_of_file>ve<contents_of_file> dosya bağlı olarak değişir.

Bakınız: Git commit nesnesinin dosya formatı nedir?

Ve hepsi bu kadar!

Fakat bekle! , <filename>hash hesaplaması için kullanılan bir parametre olmadığını fark ettiniz mi? İçerikleri oluşturuldukları tarih ve saat ile adlarından aynı kayıtsızsa, iki dosya potansiyel olarak aynı hash'e sahip olabilir. Bu, Git'in hareketleri işlemesinin ve diğer sürüm kontrol sistemlerinden daha iyi yeniden adlandırmasının nedenlerinden biridir.

Kendin Yap (Dahili)

  • Adım 6. Farklı bir dosya ile başka bir boş dosya oluşturun. filename Aynı dizinde olan
  • Adım 7. Her iki dosyanın karmalarını karşılaştırın.

Not:

Bağlantı, treenesnenin nasıl karma hale getirildiğinden bahsetmez . Algoritma ve parametrelerden emin değilim, ancak gözlemime göre muhtemelen içerdiği tüm blobsve trees(muhtemelen hash'lerine) dayalı bir hash hesaplıyor


SHA1("blob" + <size_of_file>- blob ve boyut arasında ek boşluk karakteri var mı? Boyut ondalık mı? Sıfır önekli mi?
osgx

1
@osgx Var. Referans ve benim testlerim bunu doğruluyor. Cevabı düzelttim. Boyut, önek olmadan tamsayı olarak bayt sayısı gibi görünüyor.
Samuel Harmer

13

git hash-object

Bu, test yönteminizi doğrulamanın hızlı bir yoludur:

s='abc'
printf "$s" | git hash-object --stdin
printf "blob $(printf "$s" | wc -c)\0$s" | sha1sum

Çıktı:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

sha1sumGNU Coreutils'te nerede .

Ardından, her bir nesne türünün biçimini anlamaya gelir. Önemsiz olanı zaten ele aldık blob, işte diğerleri:


Önceki bir cevapta belirtildiği gibi, uzunluk yerine olarak hesaplanmalıdır $(printf "\0$s" | wc -c). Eklenen boş karaktere dikkat edin. Diğer bir deyişle, dizge önüne boş karakter eklenmiş 'abc' ise uzunluk 3 değil 4 verir. Daha sonra sha1sum içeren sonuçlar git hash-object ile eşleşir.
Michael Ekoka

Haklısın, eşleşiyorlar. Görünüşe göre burada echo -e yerine printf kullanmanın zararlı bir yan etkisi var. Git hash-object 'abc' dizesini içeren bir dosyaya uyguladığınızda, 8baef1b ... f903 elde edersiniz ki bu printf yerine echo -e kullandığınızda elde ettiğiniz şeydir. Echo -e'nin bir dizgenin sonuna yeni bir satır eklemesi koşuluyla, davranışı printf ile eşleştirmek için aynısını yapabilirsiniz (yani s = "$ s \ n").
Michael Ekoka

3

Dayanarak Leif Gruenwoldt cevap, burada bir kabuk işlevi yerine etmektir git hash-object:

git-hash-object () { # substitute when the `git` command is not available
    local type=blob
    [ "$1" = "-t" ] && shift && type=$1 && shift
    # depending on eol/autocrlf settings, you may want to substitute CRLFs by LFs
    # by using `perl -pe 's/\r$//g'` instead of `cat` in the next 2 commands
    local size=$(cat $1 | wc -c | sed 's/ .*$//')
    ( echo -en "$type $size\0"; cat "$1" ) | sha1sum | sed 's/ .*$//'
}

Ölçek:

$ echo 'Hello, World!' > test.txt
$ git hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d
$ git-hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d

3

Python 3'teki bazı birim testleri için buna ihtiyacım vardı, bu yüzden burada bırakayım dedim.

def git_blob_hash(data):
    if isinstance(data, str):
        data = data.encode()
    data = b'blob ' + str(len(data)).encode() + b'\0' + data
    h = hashlib.sha1()
    h.update(data)
    return h.hexdigest()

Her \nyerde satır sonlarına bağlıyım ama bazı durumlarda Git bu hash'i hesaplamadan önce satır sonlarını da değiştiriyor olabilir , bu yüzden .replace('\r\n', '\n')oraya da ihtiyacınız olabilir .

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.