вторник, 17 февраля 2015 г.

Ветвление в Git

Используя ветвление, Вы отклоняетесь от основной линии разработки и продолжаете работу независимо от нее, не вмешиваясь в основную линию.

Примечание: диаграммы веток, которые приводятся ниже, взяты из оригинальной книги, поэтому номера коммитов в скриншотах и диаграммах разные, но суть от этого не меняется.

Что такое ветка?

Чтобы на самом деле разобраться в том, как Git работает с ветками, мы должны сделать шаг назад и рассмотреть, как Git хранит свои данные.

Git хранит данные не как последовательность изменений или дельт, а как последовательность снимков состояния (snapshot).

Когда вы создаёте коммит в Git'е, Git записывает в базу объект-коммит, который содержит указатель на снимок состояния, записанный ранее в индекс, метаданные автора и комментария и ноль и более указателей на коммиты, являющиеся прямыми предками этого коммита: ноль предков для первого коммита, один — для обычного коммита и несколько — для коммита, полученного в результате слияния двух или более веток.

Для наглядности давайте предположим, что у вас есть каталог, содержащий три файла, и вы хотите добавить их все в индекс и сделать коммит. При добавлении файлов в индекс для каждого из них вычислится контрольная сумма (SHA-1 хеш, о котором мы упоминали в главе 1), затем эти версии файлов будут сохранены в Git-репозиторий (Git обращается к ним как к двоичным данным), а их контрольные суммы добавятся в индекс:

B0001

Когда вы создаёте коммит, выполняя git commit, Git вычисляет контрольную сумму каждого подкаталога (в нашем случае только корневого каталога) и сохраняет эти объекты-деревья в Git-репозиторий. Затем Git создаёт объект для коммита, в котором есть метаданные и указатель на объект-дерево для корня проекта. Таким образом, Git сможет воссоздать текущее состояние, когда будет нужно.

Ваш Git-репозиторий теперь содержит пять объектов: по одному блобу для содержимого каждого из трёх файлов, одно дерево, в котором перечислено содержимое каталога и определено соответствие имён файлов и блобов, и один коммит с указателем на тот самый объект-дерево для корня и со всеми метаданными коммита.

commit-and-tree 

Если вы сделаете некоторые изменения и создадите новый коммит, то следующий коммит сохранит указатель на коммит, который шёл непосредственно перед ним. После следующих двух коммитов история может выглядеть примерно так:

commits-and-parents1

Ветка в Git'е — это просто легковесный подвижный указатель на один из этих коммитов. Ветка по умолчанию в Git'е называется master. Когда вы создаёте коммиты на начальном этапе, вам дана ветка master, указывающая на последний сделанный коммит. При каждом новом коммите она сдвигается вперёд автоматически.

На рисунке показан указатель ветки master, а так же tag созданный для последнего (третьего) коммита в этой ветке. Кроме того есть спец указатель HEAD который указывает на ветку в которой вы сейчас находитесь.

branch-and-history

Ветка master в Git это не какая-то специальная ветка. Она точно такая же, как и все остальные ветки. Единственная причина по которой она существует в каждом репозитарии, это то, что по умолчанию команда git init создает ее, и большинство людей это устраивает и они не меняют ее название.

Создание ветки

Что произойдёт, если вы создадите новую ветку? Итак, этим вы создадите новый указатель, который можно будет перемещать. Скажем, создадим новую ветку под названием testing. Это делается командой git branch:

B0002

Эта команда создаст новый указатель на тот самый коммит, на котором вы сейчас находитесь

two-branches

Откуда Git узнает, на какой ветке вы находитесь в данный момент? Он хранит специальный указатель, который называется HEAD (верхушка). Учтите, что это сильно отличается от концепции HEAD в других СКВ, таких как Subversion или CVS, к которым вы, возможно, привыкли. В Git'е HEAD это указатель на локальную ветку, на которой вы находитесь.

В данный момент вы всё ещё на ветке master. Команда git branch только создала новую ветку, она не переключила вас на неё.

head-to-master

Чтобы перейти на существующую ветку, вам надо выполнить команду git checkout. Давайте перейдём на новую ветку testing:

B0003

Можно так же легко переключится обратно на ветку master и посмотреть на что указывает HEAD, а потом снова переключится на ветку testing и опять посмотреть куда указывает HEAD.

Посмотреть можно командой git log --oneline –decorate

B0004

Как видим HEAD указывает на один и тот же коммит (0defe01), хотя мы и переключались на разные ветки.

Команда git checkout testing просто переключила указатель HEAD на ветку testing

head-to-testing

И теперь HEAD указывает на ветку testing

В чём же важность этого? Давайте подредактируем файлик test.rb  сделаем ещё один коммит:

B0005

Как видим указатель HEAD передвинулся на новый коммит 59b2c98 в ветке testing.

advance-testing

Это интересно, потому что теперь ваша ветка testing передвинулась вперёд, но ветка master всё ещё указывает на коммит, на котором вы были, когда выполняли git checkout, чтобы переключить ветки. Давайте перейдём обратно на ветку master:

B0006

Как видим HEAD теперь указывает на коммит 0defe01 в ветке мастер. И содержимое файла test.rb стало таким, каким было до нашего редактирования на предыдущем шаге. На рисунке это можно отобразить так:

checkout-master

То есть при команде git checkout указатель HEAD перемещается на другую ветку и в рабочий каталог копируется состояние последнего коммита в этой ветке.

Эта команда выполнила два действия. Она передвинула указатель HEAD назад на ветку master и вернула файлы в вашем рабочем каталоге назад, в соответствие со снимком состояния, на который указывает master. Это также означает, что изменения, которые вы делаете, начиная с этого момента, будут ответвляться от старой версии проекта. Это, по сути, откатывает изменения, которые вы временно делали на ветке testing, так что дальше вы можете двигаться в другом направлении.

Теперь снова подредактируем файлик test.rb и сделаем коммит

B0007

И так теперь HEAD указывает на коммит 5228039 в ветке master.

Теперь история вашего проекта разветвилась (см. рис. ниже). Мы создали новую ветку testing, перешли на неё, поработали на ней немного, переключились обратно на основную ветку master и выполнили другую работу. Оба эти изменения изолированы в отдельных ветках: вы можете переключаться туда и обратно между ветками и слить их, когда будете готовы. И всё это было сделано простыми командами branch и checkout.

advance-master

Историю изменений и ветвлений можно посмотреть в псевдографическом виде в Git c помощью команды

$ git log --oneline --decorate --graph –all

B0008

Из-за того, что ветка в Git'е на самом деле является простым файлом, который содержит 40 символов контрольной суммы SHA-1 коммита, на который он указывает, создание и удаление веток практически беззатратно. Создание новой ветки настолько же быстрое и простое, как запись 41 байта в файл (40 символов + символ новой строки).

1 комментарий: