Как отмечают спектры в комментарии , хеши действительны везде.
В большинстве случаев Git просто использует имя для поиска хэша. Таким образом, имя ветви, такое как develop
или feature1
, просто читаемое человеком, и, что важно, изменяемое - имя для какого-то конкретного хеш-идентификатора. Это хеш-идентификатор - это в основном то, что заботит Git. Команда, подобная git log
, преобразует имя в хэш-идентификатор и начинает работать с коммитом. Сам коммит содержит еще один хеш-идентификатор для родительского коммита, и git log
будет проверять этого коммита, который имеет еще один хэш-идентификатор и т. Д.
Мы можем связать каждый из этих коммитов в обратном направлении, используя имя ветви, чтобы найти последний коммит, например:
... <-grandparent <-parent <-0fc0d3 <--branchname
и это по сути то, что делает Git.
При использовании операции с диапазоном, такой как b0a710ad5..0fc0d3
, Git идет прямо к хэш-идентификатору: 0fc0d3
является положительной ссылкой , поэтому Git находит этот коммит. Из-за двух точек ..
между ними первый хэш-идентификатор b0a710ad5
синтаксически эквивалентен ^b0a710ad5
: это отрицательная ссылка . Git также находит этот коммит, но использует его для подавления , показывающего некоторые коммиты. Если b0a710ad5
находится прямо в цепочке, Git останавливается при достижении b0a710ad5
. Если b0a710ad5
не в цепочке - например, если цепочки коммитов выглядят примерно так:
b0a710ad5 ... <--somename
/
great-...-grandparent <-... <-parent <-0fc0d3 <--branchname
затем b0a710ad5
подавляет great- n th-grandparent , который Git находит, идя назад от b0a710ad5
.
Этот процесс перехода назад, начиная с более позднего коммита, определяет, как Git определяет достижимость коммита. Более ранний коммит - достижимый от некоторого более позднего коммита - или от некоторого имени - если мы начнем с более позднего коммита и будем работать в обратном направлении и достигнем более раннего коммита.
Имена иногда делают имеют значение
Обратите внимание, что когда вы делаете новый коммит, вы обычно делаете это, находясь "на" некоторой ветви. Наличие на ветке означает, что Git присоединяет имя HEAD
к этой ветке, так что если мы рисуем наши коммиты с именами веток справа, указывая на tip (большинство недавние) коммиты, к одному из этих имен прикреплено HEAD
:
...--F--G--H <-- develop
\
I--J <-- feature1 (HEAD)
(Одиночные заглавные буквы здесь обозначают фактические хеш-идентификаторы, которые слишком громоздки, чтобы их беспокоить.)
Если мы делаем новый коммит сейчас, Git устанавливает новый коммит в хэш-идентификатор текущего коммита J
, а затем записывает хэш-идентификатор нового коммита в имя feature1
. Это то, что HEAD
присоединено к feature1
, а feature1
в настоящее время имена передают J
, вот как все это работает. Git делает новый коммит K
:
...--F--G--H <-- develop
\
I--J <-- feature1 (HEAD)
\
K [make new commit: parent is HEAD]
затем записывает K
в feature1
, чтобы получить:
...--F--G--H <-- develop
\
I--J
\
K <-- feature1 (HEAD)
который мы можем перерисовать на меньшее количество строк:
...--F--G--H <-- develop
\
I--J--K <-- feature1 (HEAD)
Обратите внимание, что HEAD
остается подключенным к feature1
на протяжении всей операции; меняется feature1
, чтобы сохранить новый хэш-идентификатор.
(Когда вы используете git push
, вы также используете эти имена, чтобы идентифицировать коммиты, которые вы хотите отправить в какой-либо другой Git-репозиторий. Вы просите их установить их имена на основе вашего names. Когда вы используете git fetch
, некоторые other Git имеют имена, которые указывают на коммиты, имеющие хэш-идентификаторы, а ваш Git копирует commits - который сохраняет тот же хеш ID - но затем записывает новые origin/*
имена в ваш репозиторий, чтобы запоминать их имена, не вступая в конфликт с именами ваших ветвей. Таким образом, имена имеют значение и здесь - но хеш-идентификаторы имеют значение, по крайней мере, так же, как и «настоящие имена» фактических коммитов.)