Яким чином хитовий файл обчислює файли git?


124

Хеші SHA1, що зберігаються в дерев яних об'єктах (як повернуто git ls-tree), не відповідають хешам SHA1 файлового вмісту (як повертається sha1sum)

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

Яким чином хитовий файл обчислює файли git? Чи стискає він вміст перед обчисленням хеша?



1
Докладніше див. Також у progit.org/book/ch9-2.html
netvope

5
Здається, посилання netvope зараз мертва. Я думаю, що це нове місце розташування: git-scm.com/book/en/Git-Internals-Git-Objects, яке є § 9.2 від git-scm.com/book
Rhubbarb

Відповіді:


122

Git префіксує об'єкт "blob", за яким слід довжина (як цілком зрозуміле для людини ціле число), а потім символ NUL

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

Джерело: http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html


2
Також варто згадати, що він замінює "\ r \ n" на "\ n", але залишає ізольовані "\ r" s в спокої.
user420667

8
^ виправлення вище коментаря: іноді git робить заміну вище, залежно від налаштувань eol / autocrlf.
користувач420667

5
Ви також можете порівняти це з виходом echo 'Hello, World!' | git hash-object --stdin. За бажанням можна вказати, --no-filtersщоб переконатися, що не відбувається перетворення crlf, або вказати, --path=somethi.ngщоб git використовував вказаний фільтр gitattributes(також @ user420667). І -wнасправді подати блоб .git/objects(якщо ви перебуваєте в git repo).
Тобіас Кіенцлер

Висловлюючи еквівалентність, щоб мати сенс: echo -e 'blob 16\0Hello, \r\nWorld!' | shasum ==, echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters і це також буде еквівалентно \nі 15.
Пітер Краус

1
echoдодає новий рядок до виводу, який також передається в git. Ось чому його 14 символів. Щоб використовувати echo без нового рядка, напишітьecho -n 'Hello, World!'
Bouke Versteegh,

36

Я тільки розширення на відповідь по @Leif Gruenwoldtта деталізації , що знаходиться в засланні , наданої@Leif Gruenwoldt

Зроби це сам..

  • Крок 1. Створіть порожній текстовий документ (ім'я не має значення) у вашому сховищі
  • Крок 2. Постановіть і введіть документ
  • Крок 3. Визначте хеш блобу, виконавши git ls-tree HEAD
  • Крок 4. Знайдіть хеш блобу e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • Крок 5. Вирвіться з подиву і прочитайте нижче

Як GIT обчислює хеші фіксації

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

Текст blob⎵є постійним префіксом, а \0також постійним і є NULLсимволом. <size_of_file>І <contents_of_file>варіюються в залежності від файлу.

Див.: Який формат файлу об'єкта git commit?

І це всі люди!

Але зачекайте! , Ви помітили, що параметр <filename>не є параметром, який використовується для обчислення хешу? Два файли потенційно можуть мати один і той же хеш, якщо їхній вміст не відрізняється від дати та часу, коли вони були створені та їх імені. Це одна з причин, по якій Git рухається та перейменовується краще, ніж інші системи управління версіями.

Зроби сам (Ext)

  • Крок 6. Створіть ще один порожній файл із іншим filenameу тому самому каталозі
  • Крок 7. Порівняйте хеші обох файлів.

Примітка:

Посилання не вказує, як treeхеширується об'єкт. Я не впевнений у алгоритмі та параметрах, проте, з мого спостереження, він, ймовірно, обчислює хеш на основі всіх blobsі trees(їх хешей, ймовірно), що він містить


SHA1("blob" + <size_of_file>- чи є додатковий пробільний характер між крапом і розміром? Чи розмір десятковий? Це нульовий префікс?
osgx

1
@osgx Є. Довідка та моє тестування підтверджують це. Я виправив відповідь. Здається, що розмір байтів становить ціле число без префікса.
Самуель Хармер

13

git hash-object

Це швидкий спосіб перевірити свій метод тестування:

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

Вихід:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

де sha1sumзнаходиться в GNU Coreutils.

Потім зводиться до розуміння формату кожного типу об’єктів. Ми вже висвітлювали тривіальне blob, ось інші:


Як уже згадувалося в попередній відповіді, довжина , а повинна бути розрахована $(printf "\0$s" | wc -c). Зверніть увагу на доданий порожній символ. Тобто, якщо рядок 'abc' з доданим порожнім символом попереду, довжина отримала б 4, а не 3. Тоді результати з sha1sum відповідають git hash-object.
Майкл Екока

Ви маєте рацію, що вони відповідають. Здається, тут є трохи згубний побічний ефект від використання printf, а не відлуння -е. Якщо ви застосуєте git-хеш-об'єкт до файлу, що містить рядок 'abc', ви отримаєте 8baef1b ... f903, що ви отримуєте при використанні echo -e, а не printf. За умови, що echo -e додає новий рядок в кінці рядка, здається, що для відповідності поведінки з printf ви можете зробити те саме (тобто s = "$ s \ n").
Майкл Екока

3

Виходячи з відповіді Лейфа Грюнвольдта , ось функція оболонки замінює 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/ .*$//'
}

Тест:

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

3

Мені це знадобилося для деяких тестів на модулі в Python 3, тому я подумав, що залишу його тут.

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()

Я дотримуюся \nзакінчень рядків скрізь, але за деяких обставин Git також може змінити закінчення рядків, перш ніж обчислити цей хеш, тож вам може знадобитися і .replace('\r\n', '\n')там.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.