Git не имеет "ствола" (хотя вы можете притворяться, что любая конкретная ветвь является вашим "стволом", если хотите).
В этом отношении, в некотором смысле, в Git нет ветвейили.Это действительно все зависит от того, как вы определяете ответвление .См. Что именно мы подразумеваем под "ветвью"? Конечно, если мы определим слово branch более полезным способом, Git имеет ветви.Они просто очень отличаются от веток SVN.
Интересно, что у самого SVN тоже нет веток, в некотором смысле.Вот цитата из книги Red Bean :
Вы должны запомнить два важных урока из этого раздела.Во-первых, Subversion не имеет внутренней концепции ветки - она знает только, как делать копии.Когда вы копируете каталог, результирующий каталог является только «ветвью», потому что вы добавляете к нему это значение.Вы можете думать о каталоге по-другому, или обращаться с ним по-другому, но для Subversion это просто обычный каталог, который содержит некоторую дополнительную историческую информацию.
Что касается Git, важно помнить, чтоGit все о совершает .Коммит является основным лейтмотивом Git.Каждый коммит содержит полный снимок вашего исходного кода, а также некоторые метаданные: информация о коммите.Каждый коммит имеет уникальный хэш-идентификатор: 1 , зарезервированный для , для которого commit и только для этого коммита .Например, b697d92f56511e804b8ba20ccbe7bdc85dc66810
- это коммит Git 2.22 в любом клоне репозитория Git для самого Git.Никакой другой коммит нигде не будет иметь такого хеш-идентификатора. 2
Поскольку каждый коммит имеет уникальный хэш-идентификатор, мы - или, по крайней мере, Git - можем поговорить или найтификсация только с идентификатором хэша.В общем, каждый коммит хранит в своих метаданных хэш-идентификатор какого-то другого более раннего коммита.В частности, более ранний коммит, который больше всего интересует нас - или Git - это хэш-идентификатор непосредственного родителя коммита: тот, который приходит прямо перед коммитом.
Обратите внимание, что когда мы делаем новый коммит, мы знаем , какой именно коммит должен прийти перед новым коммитом, который мы делаем сейчас.Это потому, что мы делаем новый коммит, проверяя, с git checkout
, некоторый существующий коммит.Затем мы работаем над этим коммитом, выполняем наши команды git add
и запускаем git commit
.Новый коммит должен иметь в качестве родителя хеш-идентификатор коммита, который мы извлекли.
Давайте кратко рассмотрим, как это работает.Но хеш-идентификаторы коммитов большие и уродливые и невозможны для простых людей (кроме как с помощью вырезания и вставки), поэтому давайте использовать эти заглавные буквы, чтобы заменить эти коммиты.Давайте возьмем простой крошечный репозиторий с тремя коммитами и назовем их A
, B
и C
, как мы сделали их в этом порядке.Каждый коммит хранит хеш-идентификатор своего родителя, который составляет обратную цепочку коммитов:
A <-B <-C
То есть, коммит C
говорит мой родитель B
и B
говорит мой родитель A
.A
не имеет родителей - не может, это был первый коммит.Технический термин для этого таков: A
- это корневой коммит , и вы увидите [root commit]
, напечатанный Git, когда вы сделаете самый первый коммит в новом репозитории. 3
Если мы проверим коммит C
, поработаем с ним, а затем запустим git commit
, Git:
- упакует наши обновленные файлы, чтобы перейти вновый снимок;
- собирать наши метаданные: имя и адрес электронной почты, дату и время этой новой фиксации и т. д., а также наше сообщение журнала, чтобы напомнить нам позже почему мы сделалиэтот новый коммит;
- вставляет хеш-идентификатор коммита
C
в качестве родителя этого нового коммита тоже в метаданные;и - записать новый коммит, получив новый и уникальный большой уродливый хеш-код.
Потому что new commit D
Родитель C
, это автоматически расширяет цепочку:
A <-B <-C <-D
Конечно, D
получает идентификатор хеша коммита, который является большим и уродливым и уникальным для D
, и нет никакого способа , который мы могли бы запомнить. Но нам не нужно помнить это: для этого компьютер . Давайте запомним компьютер , сохранив фактический хеш-идентификатор в каком-нибудь имени.
Это имя - обычно - имя ветви , как master
. То есть, прежде чем мы сделаем D
, картинка действительно выглядит так:
A--B--C <-- master
Имя master
запоминает фактический хэш-идентификатор коммита C
. Мы запускаем git checkout master
, а Git использует имя для нахождения идентификатора и идентификатор для нахождения коммита, который переводит нас в наше рабочее дерево, чтобы мы могли с ним работать. 4
Мы выполняем нашу работу, запускаем git add
и git commit
и делаем новый коммит D
. последний шаг git commit
заключается в том, что Git записывает фактический хэш-идентификатор D
- каким бы он ни был - в имя master
:
A--B--C--D <-- master
master
больше не помнит C
, а скорее D
; но это нормально, потому что D
помнит C
. Раньше master
вспоминали A
; затем он вспомнил B
, который вспомнил A
; потом вспомнил C
. Теперь он помнит D
.
Чтобы создать новую ветку в Git, вы просто указываете ей создать новое имя, , указывающее на какой-нибудь существующий коммит , например:
A--B--C--D <-- master, feature
Хитрость заключается в том, чтобы Git запомнил, какое имя ветви обновлять при запуске git commit
, и именно здесь появляется специальное имя HEAD
со всеми заглавными буквами. Когда вы запускаете git checkout master
, Git прикрепляет имя HEAD
к имени master
:
A--B--C--D <-- master (HEAD), feature
Когда вы запускаете git checkout feature
, Git присоединяет HEAD
к feature
вместо:
A--B--C--D <-- master, feature (HEAD)
Обе команды извлечения извлекают коммит D
, где хранятся все ваши файлы. И оба branch имеют все четыре коммита, на данный момент - но если вы на feature
и делаете новый коммит сейчас, то этот новый коммит получает новый большой уродливый хеш-идентификатор, который мы назовем E
. Новый коммит имеет D
в качестве родителя, и в качестве последнего шага git commit
, Git записывает хэш-идентификатор E
в feature
, потому что именно там присоединено HEAD
:
A--B--C--D <-- master
\
E <-- feature (HEAD)
Если мы теперь запустим git checkout master
, Git вернет коммит D
обратно в наше рабочее дерево и присоединит HEAD
к master
. Если мы сейчас сделаем еще один новый коммит, родитель нового коммита снова будет D
, и master
будет обновлено на этот раз:
F <-- master (HEAD)
/
A--B--C--D
\
E <-- feature
Эти коммиты - эти обращенные назад цепочки - образуют граф , технически направленный ациклический граф. Имена ветвей, такие как master
и feature
, по сути, являются просто метками , указывающими на определенные узлы в графе. Когда вы говорите «функция ветвления», будьте осторожны с тем, имеете ли вы в виду имя feature
, или коммит E
, или коммит E
+ D
+ C
+ B
+ A
или что-то еще.
Резюме
В Git, имя ветви - это просто метка, указывающая на один конкретный коммит. Саму ветвь можно найти, начиная с этого коммита и работая в обратном направлении по графику. Граф состоит из коммитов и их родительских связей.
В SVN, как говорится в книге Red Bean, ветвь - это копия каталога (плюс некоторые метаданные). Это не относится к Git - слово branch является неоднозначным и иногда означает name , короткую метку, содержащую идентификатор хеша коммита, а иногда означает некоторое подмножество общего зафиксировать граф, найденный, начиная с хеш-идентификатора в имени . Когда под "ветвью" мы подразумеваем "некоторый набор коммитов", то это те коммиты , а не какой-то каталог.
1 Эти хеш-идентификаторы на самом деле являются криптографическими контрольными суммами данных фиксации (снимка и метаданных). Из-за этого ни один коммит не может быть изменен. Если вы на самом деле берете необработанные данные коммита - что вы можете через git cat-file -p
; попробуйте git cat-file -p HEAD
некоторое время - и внесите в него некоторые изменения и сохраните его обратно в Git, в результате вы получите новый и другой коммит со своим новым, уникальным хеш-идентификатором. Вы можете думать о Git как о базе данных только для добавления, содержащей коммиты. Это не совсем верно, но это хорошее первое приближение.
2 Технически это означает, что никакой Git-репозиторий-Git не может повторно использовать этот хэш-идентификатор. Ваш собственный проект, если он не является источником для Git и никогда не подключается к Git-репозиторию для Git, может использовать этот хэш-идентификатор. Очень велики шансы, что этого никогда не произойдет: каждый сделанный вами коммит имеет только 1 из 2 160 шансов для нас , что хэш-ID. 2 160 - очень большое число: 1 461 501 637 330 302 918 203 684 832 716 283 019 655 932 542 976 или около полутора квиндециллионов по короткой шкале .
3 A имя ветви должно указывать на какой-либо существующий коммит. Это порождает проблему: в новом и полностью пустом хранилище нет коммитов, поэтому не может быть ветвей. Это акт создания первого коммита - корневого коммита нового репозитория - который создает начальное имя ветки, обычно master
. Вы можете сделать новые корневые коммиты позже, используя git checkout --orphan
или git fetch
для другого репозитория Git, который имеет несвязанную историю, но типичный репозиторий Git имеет тенденцию иметь только один корневой коммит. Эта одна корневая фиксация находится на каждой ветви!
4 Из-за хеш-идентификатора криптографической контрольной суммы каждый коммит замораживается на все время. Но это также верно для снимка, который сохраняет каждый коммит. Файлы «внутри» коммита, которые технически представляют собой просто больше объектов в базе данных объектов Git, также заморожены навсегда и хранятся в специальном сжатом формате только для чтения, только для Git , Поскольку они доступны только для чтения, они могут быть общими для разных коммитов, что экономит много места: у вас может быть проект с 4000 файлами, вы можете изменить 2 и сделать новый коммит. Git повторно сохраняет каждый файл в новом коммите, но 3998 из них фактически просто повторно используют предыдущий снимок: есть только 2 новых снимка.
Это отлично подходит для архивирования, но бесполезно для выполнения реальной работы. Все эти файлы представлены в виде «только для Git», и вашему компьютеру нужны его файлы в обычной повседневной форме. Таким образом, Git должен распаковать высушенные сублимацией файлы, повторно их увлажнив. Эти восстановленные копии попадают в ваше рабочее дерево , где вы можете их просматривать и работать с ними. Вы никогда не работаете непосредственно с копиями в Git.
Этот процесс сублимационной сушки и регидратации файлов использует промежуточную область, которую Git иногда называет промежуточной областью , или иногда index . Эти два слова являются терминами для одной и той же вещи. Важно понимать, что в индексной / промежуточной области есть копии каждого файла из текущего коммита, и что Git делает новые коммиты из индекса / промежуточной области, а не из работы. -дерево, но мы не будем углубляться в это здесь.