Защо git hash-object връща различен хеш от openssl sha1?

Контекст: Изтеглих файл (Audirvana 0.7.1.zip) от code.google към моя Macbook Pro (Mac OS X 10.6.6).

Исках да проверя контролната сума, която за този конкретен файл е публикувана като 862456662a11e2f386ff0b24fdabcb4f6c1c446a (SHA-1). git hash-object ми даде различен хеш, но openssl sha1 върна очаквания 862456662a11e2f386ff0b24fdabcb4f6c1c446a.

Следният експеримент изглежда изключва всякакви възможни повреди при изтегляне или разлики в новия ред и показва, че всъщност има два различни алгоритъма в игра:

$ echo A > foo.txt
$ cat foo.txt
A
$ git hash-object foo.txt 
f70f10e4db19068f79bc43844b49f3eece45c4e8
$ openssl sha1 foo.txt 
SHA1(foo.txt)= 7d157d7c000ae27db146575c08ce30df893d3a64

Какво става?


person twcamper    schedule 13.03.2011    source източник
comment
Има добра статия за това на progit.org/book/ch9-2.html   -  person Josh Lee    schedule 14.03.2011
comment
Местоположението на книгата е променено: git-scm.com/book/ch9 -2.html#Object-Storage (и не мога да редактирам коментари за SO).   -  person riezebosch    schedule 21.03.2014
comment
Възможен дубликат на Присвояване на Git SHA1 без Git || stackoverflow.com/questions/7225313/   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 17.05.2016


Отговори (4)


Виждате разлика, защото git hash-object не просто взема хеш на байтовете във файла - той добавя низа "blob", последван от размера на файла и NUL към съдържанието на файла преди хеширането. Има повече подробности в този друг отговор на Stack Overflow:

Или, за да се убедите, опитайте нещо като:

$ echo -n hello | git hash-object --stdin
b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0

$ printf 'blob 5\0hello' > test.txt
$ openssl sha1 test.txt
SHA1(test.txt)= b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0
person Mark Longair    schedule 13.03.2011
comment
Защо авторите на git са избрали това поведение? - person liori; 13.03.2011
comment
liori: Мога само да предполагам. Добавих отговор, показващ как се използва в един специален случай, но се съмнявам, че това е единствената причина. - person araqnid; 13.03.2011
comment
@liori: Предполагам, че е за да се уверите, че нямате петно, което има същото име на обект (SHA1sum) като комит или дърво и т.н. - всеки има (поне) своя тип, добавен преди хеша изчислено. - person Mark Longair; 13.03.2011
comment
Също така петното ‹filesize›\0 (или подобно) в началото на файла означава, че можете да разберете типа на обекта много бързо само чрез декомпресиране на първите байтове на обектния файл. Има повече за компресирането и какво всъщност се записва на диска в този раздел на хубавата глава на Pro Git върху Git обекти. - person Mark Longair; 13.03.2011
comment
@liori: има смисъл git да използва sha-1 по този начин, тъй като целта му е контрол на версиите на файловите дървета, което не е целта на cmd line utils като sha1sum или md5sum. - person twcamper; 13.03.2011
comment
@liori: Всички типове обекти в git (блобове, комити, тагове и дървета) са именувани с хеш. Има команда cat-file -t, например git cat-file -t a7bb6fb0 ви казва типа на обекта, чието име (хеш) започва с a7bb6fb0... Може да направи това, защото действителният обект (съхранен в хранилището, компресиран) започва с blob или дърво или каквото и да е друго. Можете да видите обекта с команда като python -c "import zlib,sys;print repr(zlib.decompress(sys.stdin.read()))" < .git/objects/a7/bb6fb0*. Както и да е, обобщението е, че името на git е хешът на git обекта, а не само петното вътре. - person ShreevatsaR; 07.08.2013

Дайджестът SHA1 се изчислява върху заглавен низ, последван от данните на файла. Заглавието се състои от типа на обекта, интервал и дължината на обекта в байтове като десетичен знак. Това е отделено от данните с нулев байт.

So:

$ git hash-object foo.txt
f70f10e4db19068f79bc43844b49f3eece45c4e8
$ ( perl -e '$size = (-s shift); print "blob $size\x00"' foo.txt \
               && cat foo.txt ) | openssl sha1
f70f10e4db19068f79bc43844b49f3eece45c4e8

Една последица от това е, че "празното" дърво и "празният" петно ​​имат различни идентификатори. Това е:

e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 винаги означава "празен файл" 4b825dc642cb6eb9a060e54bf8d69288fbee4904 винаги означава "празна директория"

Ще откриете, че всъщност можете да правите git ls-tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 в ново хранилище на git без регистрирани обекти, защото се разпознава като специален случай и никога не се съхранява (с модерните версии на Git). За разлика от това, ако добавите празен файл към вашето репо, ще бъде съхранен петно ​​"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391".

person araqnid    schedule 13.03.2011

Git съхранява обекти като [Тип на обекта, Дължина на обекта, разделител (\0), Съдържание] Във вашия случай:

$ echo "A" | git hash-object --stdin
f70f10e4db19068f79bc43844b49f3eece45c4e8

Опитайте се да изчислите хеша като:

$ echo -e "blob 2\0A" | shasum 
f70f10e4db19068f79bc43844b49f3eece45c4e8  -

Забележете използването на -e (за bash shell) и коригиране на дължината за нов ред.

person Andrei Emeltchenko    schedule 10.09.2020

Отговорът се крие тук:

Как да присвоите Git SHA1 на файл без Git?

git изчислява върху файлови метаданни + съдържание, не само съдържание.

Това е достатъчно добър отговор за сега и изводът е, че git не е инструментът за изтегляне на контролна сума.

person twcamper    schedule 13.03.2011