Как git вычисляет хэши файлов? - PullRequest
117 голосов
/ 29 августа 2011

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

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

Как git вычисляет хэши файлов? Сжимает ли содержимое перед вычислением хэша?

Ответы [ 5 ]

110 голосов
/ 29 августа 2011

Git ставит перед объектом «blob», после которого следует длина (в виде читаемого человеком целого числа), за которым следует символ NUL

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

Источник: http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html

33 голосов
/ 05 марта 2015

Я только расширяю ответ на @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 (их хэши, вероятно), которые он содержит

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, вот другие:

3 голосов
/ 14 мая 2017

Мне это понадобилось для некоторых юнит-тестов в 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') там же.

3 голосов
/ 27 июня 2016

Основано на ответе Лейф Грюнвольдт . Вот функция оболочки, заменяющая 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
...