Что такое идентификатор коммита Git?
Как генерируются идентификаторы коммитов Git для уникальной идентификации коммитов?
Пример: 521747298a3790fde1710f3aa2d03b55020575aa
Как это работает? Они уникальны только для каждого проекта? Или для Git-репозиториев по всему миру?
2 ответа
Идентификатор коммита Git - это хэш SHA-1 всех важных вещей в коммите. Я не собираюсь перечислять их все, но вот важные...
- Содержание, все это, а не только разница.
- Дата фиксации.
- Имя коммиттера и адрес электронной почты.
- Журнал сообщений.
- Идентификатор предыдущего коммита (ов).
Измените что-либо из этого, и идентификатор фиксации изменений. И да, один и тот же коммит с одинаковыми свойствами будет иметь одинаковый идентификатор на другом компьютере. Это служит трем целям. Во-первых, это означает, что система может определить, был ли изменен коммит. Это испечено прямо в архитектуру.
Во-вторых, можно быстро сравнить коммиты, просто взглянув на их идентификаторы. Это делает сетевые протоколы Git очень эффективными. Хотите сравнить два коммита, чтобы увидеть, одинаковы ли они? Не нужно присылать всю разницу, просто отправьте идентификаторы.
В-третьих, и это гениально, два коммита с одинаковыми идентификаторами имеют одинаковую историю. Вот почему идентификатор предыдущих коммитов является частью хэша. Если содержимое коммита одинаково, но его родители разные, идентификатор коммита должен отличаться. Это означает, что при сравнении репозиториев (как в режиме push или pull), когда Git находит общий коммит между двумя репозиториями, он может прекратить проверку. Это делает толкание и вытягивание чрезвычайно эффективным. Например...
origin
A - B - C - D - E [master]
A - B [origin/master]
Сетевой разговор для git fetch origin
идет что-то вроде этого...
local
Эй, происхождение, какие ветви у вас есть?origin
У меня есть мастер в E.local
У меня нет E, у меня есть твой хозяин в B.origin
Б ты говоришь? У меня есть B, и это предок E. Это подтверждается. Позвольте мне отправить вам C, D и E.
Именно поэтому, когда вы переписываете коммит с rebase, все после него должно измениться. Вот пример.
A - B - C - D - E - F - G [master]
Допустим, вы переписали D, просто чтобы немного изменить сообщение журнала. Теперь D больше не может быть D, его нужно скопировать в новый коммит, который мы назовем D1.
A - B - C - D - E - F - G [master]
\
D1
Хотя D1 может иметь C в качестве родителя (C не затронут, коммиты не знают своих потомков), он отключен от E, F и G. Если мы изменим родителя E на D1, E больше не будет E. Он должен быть скопирован в новый коммит E1.
A - B - C - D - E - F - G [master]
\
D1 - E1
И так далее с F до F1 и G до G1.
A - B - C - D - E - F - G
\
D1 - E1 - F1 - G1 [master]
Все они имеют один и тот же код, просто разные родители (или, в случае D1, другое сообщение о коммите).
Вы можете точно узнать, что входит в создание идентификатора коммита, запустив
git cat-file commit HEAD
Это даст вам что-то вроде
tree 07e239f2f3d8adc12566eaf66e0ad670f36202b5
parent 543a4849f7201da7bed297b279b7b1e9a086a255
author Justin Howard <justin.howard@example.com> 1426631449 -0700
committer Justin Howard <justin.howard@example.com> 1426631471 -0700
My commit message
Это дает вам:
- Контрольная сумма содержимого дерева
- Идентификатор родительского коммита (если это слияние, будет больше родителей)
- Автор коммита с отметкой времени
- Фиксатор коммита с отметкой времени
- Сообщение коммита
Git берет все это и делает хэш sha1. Вы можете воспроизвести идентификатор коммита, выполнив
(printf "commit %s\0" $(git cat-file commit HEAD | wc -c); git cat-file commit HEAD) | sha1sum
Это начинается с печати строки commit
сопровождаемый пробелом и количеством байтов cat-file
текстовый блоб Затем добавляет cat-file
BLOB-объект, за которым следует нулевой байт. Все это затем проходит через sha1sum
,
Как видите, в этой информации нет ничего, что бы идентифицировало проект или репозиторий. Причина, по которой это не вызывает проблем, заключается в том, что астрономически маловероятно, что два разных хеша коммитов столкнутся.