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
,
2 ответа
Правильный ответ на вопрос в теме:
Git объекты SHA-1 - это содержимое файла или имена файлов?
скорее всего, "ни один", поскольку вы ссылались на содержимое свободного объектного файла, а не на исходный файл - и даже если вы ссылались на оригинальный файл, это все еще не совсем верно.
Свободный объект в Git - это простой файл. Имя файла составляется из хеш-идентификатора объекта. Хэш-идентификатор объекта, в свою очередь, создается путем вычисления хэша содержимого объекта с присоединенным префиксным заголовком.
Заголовок с префиксом зависит от типа объекта. Есть четыре типа: blob
, commit
, tag
, а также tree
, Заголовок состоит из байтовой строки с нулевым символом в конце, состоящей из имени типа в виде байтовой строки ASCII (или, что то же самое, UTF-8), за которым следует пробел, за которым следует десятичное представление размера объекта в байтах с последующим по ASCII NUL (b'\x00'
в Python, если вы предпочитаете современные нотации Python, или '\0'
если вы предпочитаете с).
После заголовка следует фактическое содержимое объекта. Итак, для файла, содержащего строку байтов 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, и результат не совпадает на этом уровне; неясно, соответствует ли zitb deflate в Git 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, который делает это в моем общедоступном репозитории "scripts" под githash.py.
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 Internals " от Джеффа Канкла, слайд 8, для иллюстрации реального содержимого BLOB-объекта: