Для точного понимания механизма ветвлений, необходимо вернуться назад и изучить то, как Git хранит данные.
Как вы можете помнить из ch01-getting-started.asc, Git не хранит данные в виде последовательности изменений, он использует набор снимков (snapshot).
Когда вы делаете коммит, Git сохраняет его в виде объекта, который содержит указатель на снимок (snapshot) подготовленных данных. Этот объект так же содержит имя автора и email, сообщение и указатель на коммит или коммиты непосредственно предшествующие данному (его родителей): отсутствие родителя для первоначального коммита, один родитель для обычного коммита, и несколько родителей для результатов слияния двух и более веток.
Предположим, у вас есть каталог с тремя файлами и вы добавляете их все в индекс и создаёте коммит. Во время индексации вычисляется контрольная сумма каждого файла (SHA-1 как мы узнали из ch01-getting-started.asc), затем каждый файл сохраняется в репозиторий (Git называет такой файл блоб — большой бинарный объект), а контрольная сумма попадёт в индекс:
$ git add README test.rb LICENSE
$ git commit -m 'Initial commit'
Когда вы создаёте коммит командой git commit
, Git вычисляет контрольные суммы каждого подкаталога (в нашем случае, только основной каталог проекта) и сохраняет его в репозитории как объект дерева каталогов.
Затем Git создаёт объект коммита с метаданными и указателем на основное дерево проекта для возможности воссоздать этот снимок в случае необходимости.
Ваш репозиторий Git теперь хранит пять объектов: три блоб объекта (по одному на каждый файл), объект дерева каталогов, содержащий список файлов и соответствующих им блобов, а так же объект коммита, содержащий метаданные и указатель на объект дерева каталогов.
Если вы сделаете изменения и создадите ещё один коммит, то он будет содержать указатель на предыдущий коммит.
Ветка в Git — это простой перемещаемый указатель на один из таких коммитов.
По умолчанию, имя основной ветки в Git — master
.
Как только вы начнёте создавать коммиты, ветка master
будет всегда указывать на последний коммит.
Каждый раз при создании коммита указатель ветки master
будет передвигаться на следующий коммит автоматически.
Note
|
Ветка «master» в Git — это не какая-то особенная ветка.
Она точно такая же, как и все остальные ветки.
Она существует почти во всех репозиториях только лишь потому, что её создаёт команда |
Что же на самом деле происходит при создании ветки?
Всего лишь создаётся новый указатель для дальнейшего перемещения.
Допустим вы хотите создать новую ветку с именем testing
.
Вы можете это сделать командой git branch
:
$ git branch testing
В результате создаётся новый указатель на текущий коммит.
Как Git определяет, в какой ветке вы находитесь?
Он хранит специальный указатель HEAD
.
Имейте ввиду, что в Git концепция HEAD
значительно отличается от других систем контроля версий, которые вы могли использовать раньше (Subversion или CVS).
В Git — это указатель на текущую локальную ветку.
В нашем случае мы всё ещё находимся в ветке master
.
Команда git branch
только создаёт новую ветку, но не переключает на неё.
Вы можете легко это увидеть при помощи простой команды git log
, которая покажет вам куда указывают указатели веток.
Эта опция называется --decorate
.
$ git log --oneline --decorate
f30ab (HEAD -> master, testing) Add feature #32 - ability to add new formats to the central interface
34ac2 Fix bug #1328 - stack overflow under certain conditions
98ca9 Initial commit
Здесь можно увидеть указывающие на коммит f30ab
ветки: master
и testing
.
Для переключения на существующую ветку выполните команду git checkout
.
Давайте переключимся на ветку testing
:
$ git checkout testing
В результате указатель HEAD
переместится на ветку testing
.
Какой в этом смысл? Давайте сделаем ещё один коммит:
$ vim test.rb
$ git commit -a -m 'made a change'
Интересная ситуация: указатель на ветку testing
переместился вперёд, а master
указывает на тот же коммит, где вы были до переключения веток командой git checkout
.
Давайте переключимся назад на ветку master
:
$ git checkout master
Note
|
git log не показывает все ветки по умолчаниюЕсли выполнить команду Ветка никуда не исчезла; просто Git не знает, что именно она вас интересует, и выводит наиболее полезную по его мнению информацию.
Другими словами, по умолчанию Для просмотра истории коммитов другой ветки необходимо явно указать её имя: |
Эта команда сделала две вещи: переместила указатель HEAD
назад на ветку master
и вернула файлы в рабочем каталоге в то состояние, на снимок которого указывает master
.
Это также означает, что все вносимые с этого момента изменения будут относиться к старой версии проекта.
Другими словами, вы откатили все изменения ветки testing
и можете продолжать в другом направлении.
Note
|
Переключение веток меняет файлы в рабочем каталоге
Важно запомнить, что при переключении веток в Git происходит изменение файлов в рабочем каталоге. Если вы переключаетесь на старую ветку, то рабочий каталог будет выглядеть так же, как выглядел на момент последнего коммита в ту ветку. Если Git по каким-то причинам не может этого сделать — он не позволит вам переключиться вообще. |
Давайте сделаем ещё несколько изменений и создадим очередной коммит:
$ vim test.rb
$ git commit -a -m 'made other changes'
Теперь история вашего проекта разошлась (см Разветвлённая история).
Вы создали ветку и переключились на неё, поработали, а затем вернулись в основную ветку и поработали в ней.
Эти изменения изолированы друг от друга: вы можете свободно переключаться туда и обратно, а когда понадобится — объединить их.
И всё это делается простыми командами: branch
, checkout
и commit
.
Все описанные действия можно визуализировать с помощью команды git log
.
Для отображения истории коммитов, текущего положения указателей веток и истории ветвления выполните команду git log --oneline --decorate --graph --all
.
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) Made other changes
| * 87ab2 (testing) Made a change
|/
* f30ab Add feature #32 - ability to add new formats to the central interface
* 34ac2 Fix bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project
Ветка в Git — это простой файл, содержащий 40 символов контрольной суммы SHA-1 коммита, на который она указывает; поэтому операции с ветками являются дешёвыми с точки зрения потребления ресурсов или времени. Создание новой ветки в Git происходит так же быстро и просто как запись 41 байта в файл (40 знаков и перевод строки).
Это принципиально отличает процесс ветвления в Git от более старых систем контроля версий, где все файлы проекта копируются в другой подкаталог. В зависимости от размера проекта, операции ветвления в таких системах могут занимать секунды или даже минуты, когда в Git эти операции мгновенны. Поскольку при коммите мы сохраняем указатель на родительский коммит, то поиск подходящей базы для слияния веток делается автоматически и, в большинстве случаев, очень прост. Эти возможности побуждают разработчиков чаще создавать и использовать ветки.
Давайте посмотрим, почему и вам имеет смысл делать так же.
Note
|
Одновременное создание новой ветки и переключение на неё
Как правило, при создании новой ветки вы хотите сразу на неё переключиться — это можно сделать используя команду |
Note
|
Начиная с Git версии 2.23, вы можете использовать
|