Как рассчитан хэш git?

Я пытаюсь понять, как git вычисляет хэш ref.

$ git ls-remote https://github.com/git/git  

....
29932f3915935d773dc8d52c292cadd81c81071d    refs/tags/v2.4.2
9eabf5b536662000f79978c4d1b6e4eff5c8d785    refs/tags/v2.4.2^{}
....

Клонировать репо локально. Проверьте refs/tags/v2.4.2^{} ref by sha

$ git cat-file -p 9eabf5b536662000f79978c4d1b6e4eff5c8d785 

tree 655a20f99af32926cbf6d8fab092506ddd70e49c
parent df08eb357dd7f432c3dcbe0ef4b3212a38b4aeff
author Junio C Hamano <[email protected]> 1432673399 -0700
committer Junio C Hamano <[email protected]> 1432673399 -0700

Git 2.4.2

Signed-off-by: Junio C Hamano <[email protected]>

Скопируйте распакованный контент, чтобы мы могли его хешировать. (AFAIK git использует несжатую версию при его хешировании)

git cat-file -p 9eabf5b536662000f79978c4d1b6e4eff5c8d785 > fi

Пусть SHA-1 содержимое с использованием git собственной хэш-команды

git hash-object fi
3cf741bbdbcdeed65e5371912742e854a035e665

Почему вывод не [9e]abf5b536662000f79978c4d1b6e4eff5c8d785? Я понимаю, что первые два символа (9e) - это длина в шестнадцатеричном формате. Как я должен хэш содержание fi, чтобы я мог получить git ref abf5b536662000f79978c4d1b6e4eff5c8d785?

Ответ 1

Как описано в " Как git commit sha1 сформирован", формула:

(printf "<type> %s\0" $(git cat-file <type> <ref> | wc -c); git cat-file <type> <ref>)|sha1sum

В случае commit 9eabf5b536662000f79978c4d1b6e4eff5c8d785 (который v2.4.2^{} и который ссылается на дерево):

(printf "commit %s\0" $(git cat-file commit 9eabf5b536662000f79978c4d1b6e4eff5c8d785 | wc -c); git cat-file commit 9eabf5b536662000f79978c4d1b6e4eff5c8d785 )|sha1sum

Это даст 9eabf5b536662000f79978c4d1b6e4eff5c8d785.

Как бы:

(printf "commit %s\0" $(git cat-file commit v2.4.2{} | wc -c); git cat-file commit v2.4.2{})|sha1sum

(все еще 9eabf5b536662000f79978c4d1b6e4eff5c8d785)

Аналогично, вычисление SHA1 тега v2.4.2 будет:

(printf "tag %s\0" $(git cat-file tag v2.4.2 | wc -c); git cat-file tag v2.4.2)|sha1sum

Это даст 29932f3915935d773dc8d52c292cadd81c81071d.

Ответ 2

Здесь немного путаницы. Git использует различные типы объектов: капли, деревья и коммиты. Следующая команда:

git cat-file -t <hash>

Сообщает вам тип объекта для заданного хэша. Таким образом, в вашем примере хеш 9eabf5b536662000f79978c4d1b6e4eff5c8d785 соответствует объекту фиксации.

Теперь, когда вы поняли себя, выполните следующее:

git cat-file -p 9eabf5b536662000f79978c4d1b6e4eff5c8d785

Дает вам содержимое объекта в соответствии с его типом (в данном случае - фиксацией).

Но это:

git hash-object fi

... вычисляет хэш для blob, содержимое которого является результатом предыдущей команды (в вашем примере), но это может быть что угодно (например, "hello world!" ). Здесь попробуйте следующее:

echo "blob 277\0$(cat fi)" | shasum

Вывод аналогичен предыдущей. Это в основном, как Git хеширует blob. Итак, хешируя fi, вы генерируете объект blob. Но, как мы видели, 9eabf5b536662000f79978c4d1b6e4eff5c8d785 является фиксацией, а не блобом. Таким образом, вы не можете использовать hash fi, чтобы получить тот же хеш.

Хеширование фиксации основано на нескольких других сведениях, которые делают его уникальным (например, коммиттер, автор, дата и т.д.). В следующей статье вы узнаете, что такое хеш-код фиксации:

Анатомия Git commit

Таким образом, вы можете получить один и тот же хеш, предоставив все данные, указанные в статье, с теми же значениями, что и в исходной фиксации.

Это может быть полезно также:

Git снизу вверх