Почему 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


Ответы (4)


Вы видите разницу, потому что git hash-object не просто берет хэш байтов в файле - он добавляет строку «blob», за которой следует размер файла и NUL, к содержимому файла перед хешированием. Более подробная информация представлена ​​в этом другом ответе на переполнение стека:

Или, чтобы убедиться, попробуйте что-нибудь вроде:

$ 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
Лиори: Я могу только предполагать. Я добавил ответ, показывающий, как он используется в одном частном случае, но я сомневаюсь, что это единственная причина. - person araqnid; 13.03.2011
comment
@liori: я думаю, это необходимо для того, чтобы убедиться, что у вас нет капли с тем же именем объекта (SHA1sum), что и фиксация или дерево и т. д. - у каждого (по крайней мере) свой тип добавлен перед хешем рассчитано. - person Mark Longair; 13.03.2011
comment
Кроме того, blob ‹filesize› \ 0 (или аналогичный) в начале файла означает, что вы можете очень быстро определить тип объекта, просто распаковав первые байты объектного файла. Подробнее о сжатии и о том, что на самом деле записано на диск, можно узнать в этом разделе хорошей главы Pro Git на объектах Git. - person Mark Longair; 13.03.2011
comment
@liori: имеет смысл, что git будет использовать sha-1 таким образом, поскольку его цель - контроль версий файловых деревьев, что не является целью утилит строки cmd, таких как 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) и настройку длины новой строки.

person Andrei Emeltchenko    schedule 10.09.2020

Ответ здесь:

Как назначить Git SHA1 файлу без Git?

git рассчитывает метаданные + содержимое файла, а не только содержимое.

На данный момент это достаточно хороший ответ, и вывод состоит в том, что git не является инструментом для подсчета контрольных сумм загрузок.

person twcamper    schedule 13.03.2011