Объекты Git SHA-1 - это содержимое файла или имена файлов?

Меня смущает то, как фактическое содержимое файла хранится в .git.

Например, Version 1 — это фактическое текстовое содержимое в test.txt. Когда я фиксирую (первый коммит) его в репо, git возвращает SHA-1 для этого файла, который находится в .git\objects\0c\15af113a95643d7c244332b0e0b287184cd049.

Когда открываю файл 15af113a95643d7c244332b0e0b287184cd049 в текстовом редакторе, то вся фигня, примерно так

x+)JMU074f040031QÐKÏ,ÉLÏË/Je¨}ºõw[Éœ„ÇR­ ñ·Î}úyGª*±8#³¨,1%>9?¯$5¯D¯¤¢„áôÏ3%³þú>š~}Ž÷*ë²-¶ç¡êÊòR“KâKòãs+‹sô

Но я не уверен, представляет ли этот мусор зашифрованную форму текста Version 1 или он представлен SHA-1 15af113a95643d7c244332b0e0b287184cd049.


person appu    schedule 10.06.2017    source источник
comment
git-scm.com/book/en/v2/Git- Внутренности-Git-объекты   -  person Oliver Charlesworth    schedule 10.06.2017
comment
это то, через что я сейчас прохожу, но я все еще не понимаю часть содержимого файла, и поэтому мне пришлось спросить здесь ..   -  person appu    schedule 10.06.2017
comment
Можете ли вы отредактировать свой вопрос, чтобы он касался какого-то конкретного аспекта описания под заголовком «Хранилище объектов» по ​​этой ссылке, которую трудно понять?   -  person jthill    schedule 10.06.2017
comment
Исследуйте объекты вашего репозитория с помощью команды git cat-file -p [sha1] и вы лучше поймете...   -  person Philippe    schedule 10.06.2017


Ответы (2)


Правильный ответ на вопрос в теме письма:

Объекты Git SHA-1 - это содержимое файла или имена файлов?

вероятно, «ни то, ни другое», поскольку вы имели в виду содержимое свободного объектного файла, а не исходный файл, и даже если вы имели в виду исходный файл, это все равно не совсем правильно.

Свободный объект в Git — это обычный файл. Имя файла создается из хэш-идентификатора объекта. Идентификатор хеша объекта, в свою очередь, создается путем вычисления хэша содержимого объекта с прикрепленным заголовком-префиксом.

Заголовок с префиксом зависит от типа объекта. Существует четыре типа: blob, commit, tag и tree. Заголовок состоит из завершающейся нулем строки байтов, состоящей из имени типа в виде строки байтов ASCII (или, что эквивалентно, UTF-8), за которым следует пробел, за которым следует десятичное представление размера объекта в байтах, за которым следует с помощью ASCII NUL (b'\x00' в Python, если вы предпочитаете современную нотацию Python, или '\0', если вы предпочитаете C).

После заголовка идет фактическое содержимое объекта. Таким образом, для файла, содержащего строку байтов b'hello\n', хэшируемые данные состоят из b'blob 6\0hello\n:

$ echo 'hello' | git hash-object -t blob --stdin
ce013625030ba8dba906f756967f9e9ca394464a
$ python3
[...]
>>> import hashlib
>>> s = b'blob 6\0hello\n'
>>> hashlib.sha1(s).hexdigest()
'ce013625030ba8dba906f756967f9e9ca394464a'

Следовательно, имя файла, которое будет использоваться для хранения этого файла, будет (производным) ce013625030ba8dba906f756967f9e9ca394464a. Как свободный объект, он становится .git/objects/ce/013625030ba8dba906f756967f9e9ca394464a.

Однако содержимое этого файла представляет собой сжатую zlib форму b'blob 6\0hello\n' (с, по-видимому, level=1 — по умолчанию в настоящее время 6, и результат не совпадает на этом уровне; неясно, является ли Git zlib deflate точно соответствует Python, но здесь сработал уровень 1):

$ echo 'hello' | git hash-object -w -t blob --stdin
ce013625030ba8dba906f756967f9e9ca394464a
$ vis .git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
x\^AK\M-J\M-IOR0c\M-HH\M-M\M-I\M-I\M-g\^B\000\^]\M-E\^D\^T$

(обратите внимание, что последний $ снова является приглашением оболочки; теперь вернемся к Python3)

>>> import zlib
>>> zlib.compress(s, 1)
b'x\x01K\xca\xc9OR0c\xc8H\xcd\xc9\xc9\xe7\x02\x00\x1d\xc5\x04\x14'
>>> import vis
>>> print(vis.vis(zlib.compress(s, 1)))
x\^AK\M-J\M-IOR0c\M-HH\M-M\M-I\M-I\M-g\^B\^@\^]\M-E\^D\^T

где vis.py это:

def vischr(byte):
    "encode characters the way vis(1) does by default"
    if byte in b' \t\n':
        return chr(byte)
    # control chars: \^X; del: \^?
    if byte < 32 or byte == 127:
        return r'\^' + chr(byte ^ 64)
    # printable characters, 32..126
    if byte < 128:
        return chr(byte)
    # meta characters: prefix with \M^ or \M-
    byte -= 128
    if byte < 32 or byte == 127:
        return r'\M^' + chr(byte ^ 64)
    return r'\M-' + chr(byte)

def vis(bytestr):
    "same as vis(1)"
    return ''.join(vischr(c) for c in bytestr)

(vis производит обратимую, но печатаемую кодировку двоичных файлов; это был мой ответ 1993 года на проблемы с cat -v).

Обратите внимание, что имена файлов, хранящиеся в репозитории Git (при фиксации), отображаются только как компоненты пути, хранящиеся в отдельных tree объектах. Вычисление хэш-идентификатора объекта дерева нетривиально; У меня есть код Python, который делает это в моем общедоступном репозитории «скриптов» в разделе githash.py< /а>.

person torek    schedule 10.06.2017
comment
По крайней мере, на этот раз более лаконично и по делу. +1 - person VonC; 10.06.2017
comment
Спасибо за хороший иллюстративный ответ, но не могли бы вы уточнить этот вопрос? Обратите внимание, что имена файлов, хранящихся в репозитории Git (в рамках фиксации), отображаются только как компоненты пути, хранящиеся в отдельных объектах дерева. пожалуйста. - person appu; 12.06.2017
comment
Коммит идентифицирует объект tree (попробуйте git cat-file -p HEAD, чтобы увидеть коммит и его древовидный объект). Сам объект дерева хранится в двоичном формате (см. приведенный мной код gitash.py), но вы можете преобразовать его в печатный текст с помощью другой команды git cat-file -p. Если дерево имеет идентификатор объекта 1234567, например, git cat-file -p 1234567 покажет его. - person torek; 12.06.2017
comment
@torek, согласно твоему ответу, я пробовал это. $ git cat-file -p 16ed вывод: 100644 blob a7036e7253f5e7099e8b68c2fe99ecf5f8b013d3 pom.xml . А затем cat pom.xml | git hash-object -t blob вывод: af86ccbdb1f4f10685ffe85cf68372109694e49a. pom.xml имеет только одну версию (не уверен, как выразить это в терминах git), поэтому нет никаких шансов, что я имею в виду другую версию pom.xml при использовании git-hash-object . Почему SHA-1 не соответствует. - person samshers; 18.09.2019
comment
@samshers: У вас есть фильтры (очистить и размазать фильтры) и/или включен взлом crlf? Если это так, используйте <filter> pom.xlm | git hash-object -t blob, поскольку хэши дерева и индекса взяты из отфильтрованного содержимого, а не содержимого рабочего дерева. (Вставьте соответствующую команду, какой бы она ни была, в качестве фильтра — например, если копия рабочего дерева имеет окончания CRLF, вы можете использовать tr -d '\015' < pom.xml в качестве фильтра.) - person torek; 18.09.2019
comment
@torek - безупречный. $ cat pom.xml | tr -d '\015' | git hash-object -t blob --stdin выводит a7036e7253f5e7099e8b68c2fe99ecf5f8b013d3 . Но еще один момент, который я хотел бы уточнить, так это то, что SHA-1 вычисляется из несжатого содержимого. Но то, что на самом деле находится в файле (в базе данных объекта), в основном представляет собой сжатое содержимое. Верно. - person samshers; 18.09.2019
comment
@samshers: да, хеш находится над несжатым содержимым (включая blob <size>\0 байт). Фактические данные в Git представляют собой либо свободный объект (сжатый zlib), либо упакованный объект (возможно, удаленный). (ой, я забыл, что в этом ответе был пример Python!) - person torek; 19.09.2019

Git Magic упоминает:

Кстати, файлы в .git/objects сжаты с помощью zlib, поэтому вам не следует смотреть на них напрямую. Отфильтруйте их с помощью zpipe -d или введите (используя git cat-file):

$ git cat-file -p .git/objects/0c/15af113a95643d7c244332b0e0b287184cd049

С zpipe:

$ ./zpipe -d < .git/objects/0c/15af113a95643d7c244332b0e0b287184cd049

Примечание: для zpipe мне пришлось сначала скомпилировать zpipe.c:

sudo apt-get install zlib1g-dev
cd /usr/share/doc/zlib1g-dev/examples
sudo gunzip zpipe.c.gz
sudo gcc -o zpipe zpipe.c -lz

Затем:

$ /usr/share/doc/zlib1g-dev/examples/zpipe -d < /usr/share/doc/zlib1g-dev/examples/zpipe -d <

Вы получите такой результат, как:

vonc@VONCAVN7:/mnt/d/git/seec$ /usr/share/doc/zlib1g-dev/examples/zpipe -d < .git/objects/0d/b6225927ef60e21138a9762c41ea0db714ca0d
blob 2142 <full content there...>

Вы видите заголовок, состоящий из типа и размера содержимого, за которым следует фактическое содержимое.

См. "Понимание внутреннего устройства Git" от Джеффа Канкла, слайд 8, иллюстрацию фактический контент блоба:

Джефф Канкл

person VonC    schedule 10.06.2017
comment
поэтому то, что находится внутри именованного файла SHA-1, представляет собой сжатую форму (с использованием zlib) заголовка + содержимого. В то время как SHA-1 вычисляется только с использованием заголовка + содержимого. Верно??? - person samshers; 19.09.2019
comment
@samshers Как говорит Торек: сжатие + дельта предназначено только для хранения объекта. SHA1 предназначен для ссылки на (несжатый) контент (+ заголовок). Вскоре это будет использовать SHA-256: stackoverflow.com/a/47838703/6309 - person VonC; 19.09.2019