Система управления версиями Git заняла прочные позиции на рынке. Как и любая система управления версиями, Git подразумевает поиск и использование более удобных и простых подходов к организации работы с исходным кодом, и его контролю.
Еще в начале 2010 года, Vincent Driessen написал статью, в которой описал опыт использования Git, и обобщил свой опыт в виде модели использования веток при организации хранения кода под управлением Git.
Свою статью он опубликовал по в своем блоге, и ее можно почитать в оригинале: A successful Git branching model.
В данном посте, я приведу свой немного вольный перевод этой статьи.
Предлагаемая модель управления исходным кодом, с использованием Git, успешно использовалась автором на всех его проектах (рабочих, и собственных). В статье не говорится о деталях проектов, а статья содержит лишь описание модели, которую автор с успехом применяет в повседневной разработке программного обеспечения.

Данная модель позволяет использовать Git именно как инструмент для контроля версий, в контексте разработки программного обеспечения.
Почему Git?
Данная статья не о Git как таковом, не о его преимуществах и недостатках. Эта статья лишь описывает одну из моделей разработки проекта, с применением особенностей Git.
Модель предложенная автором статьи базируется на особенностях работы Git с ветками версий. Для сравнения, если рассматривать классические централизованные системы контроля версий, такие как CVS/SVN, то можно увидеть более консервативный и осторожный подход к использованию веток, а также их объединения. Данные операции являются тяжелыми и сложными, а в любой книге, посвященной данным системам, такие операции описываются в разделе предназначенном для опытных пользователей. Зачастую, операции создания и объединения веток в репозитории являются привилигированными операциями, и разработчики не имеют к ним доступа.
Распределенные системы контроля версий, такие как Git, напротив, базируются на идее активного использования веток, их создания и объединения. Это позволило создать еще одну эффективную модель разработки, которая отлично подходит как для разработчика-одиночки, так и для командной разработки.
Что касается самой модели, то это не панацей, не великое открытие. Это всего лишь набор процедур и подходов, придерживаясь которых можно сделать процесс разработки ПО управляемым. Конечно, чтобы данная модель принесла свои плоды, требуется четкое соблюдение процедуры не отдельными разработчиками, а каждым участником команды. Как показала практика, и опыт применения этой модели автором на реальных проектах, модель является весьма жизнеспособной и эффективной.
Децентрализованные, но централизованные
Несмотря на долгое существование Git на рынке, как и децентрализованных систем управления версиями, до cих пор имеет место путаница в связи с централизованностью.
При разработке с использованием Git, всегда создается единый центральный репозиторий. С технической точки зрения, этот репозиторий не является центральным, так как такого понятия в Git просто не существует. Разделение на центральный, и остальные репозитории является чисто логическим, и завязано на соглашениях в конкретном проекте и команде. Центральный репозиторий, с которым работает команда, обычно называется origin.

Каждый разработчик используя push и pull может в двустороннем порядке обмениваться изменениями между своим локальным репозиторием и origin. Но, кроме двунаправленного обмена между указанными хранилищами, возможен также двусторонний обмен с хранилищами других разработчиков. Это может быть полезно, когда в проекте, над разработкой какого-либо функционала работают несколько разработчиков. Во время разработки, эти изменения не должны попадать в origin, но в то же время, должен происходить обмен изменениями, произведенными каждым из разработчиков. В этом случае, разработчики обмениваются изменениями между своими локальными репозиториями, не затрагивая код в origin.
На рисунке выше показан пример такой организации работы. В команде четыре разработчика. Каждый из них имеет доступ к origin, но помимо этого можно выделить три группы разработчиков, которые обмениваются изменениями только между собой: Алиса и Боб, Алиса и Дэвид, Клер и Дэвид.
С технической стороны, это означает, что к примеру Алиса работает с двумя удаленными репозиториями: origin и bob (который принадлежит Бобу), и наоборот.
Главные ветки

Представленная модель в значительной степени вдохновлена уже существующими моделями. Центральное хранилище предоставляет две ветки с бесконечным жизненным циклом:
masterdevelop
Ветка master в хранилище origin знакома каждому пользователю Git. Паралелльно с веткой master, существует другая ветка, называемая develop.
Ветка origin/master мы рассматриваем как главную ветку, HEAD в которой, всегда указывает на production ready состояние.
Ветка origin/develop рассматривается как главная ветка, в которой HEAD отражает состояние исходного кода, на момент последних изменений, подготовленных к следующему релизу. Некоторые называют эту ветку integration branch. Например, HEAD этой ветки используется для ночных сборок.
Когда исходный код в ветке develop достигает стабильности, и содержит уровень функциональности близкий к желаемому, все изменения из develop должны быть внесены обратно в master, и отмечены тегом соответствующего релиза.
Исходя из всего вышесказанного, можно заявить, что каждое внесение изменений в master, является очередным релизом по определению. Если строго подходить к этим рекомендациям, то теоретически, можно использовать хук(hook) к скрипту Git, для автоматической сборки и развертывания ПО на production-серверах, после каждого коммита в master.
Дополнительные ветки
В предлагаемой модели, помимо двух главных веток (master и develop), также используются вспомогательные ветки, которые позволяют облегчить ведение параллельной разработки членами команды, слежение за спектром разрабатываемого функционала проекта, подготовку к релизам, а также помогают в решении проблем, которые возникают в production коде. Но в отличии от главных веток, эти ветки имеют ограниченное время жизни, так как рано или поздно будут удалены.
В качестве основных вспомогательных типов веток могут быть выделены:
- ветки
feature - ветки
release - ветки
hotfix
(Оригинальные названия типов веток сохранены намерено, во измежание путаницы в переводе. Прим. перев.)
Каждый из этих типов веток имеет конкретные цели и правила работы с ними. Для них строго определены те ветки, от которых можно произвести ветвление, и с какими ветками должны быть объединены. И опять же, во избежание введения в заблуждение, стоит оговориться, что эти ветки никак не являются “специальными” с технической точки зрения. Это обычные ветки Git. Их типизация зависит от правил, которые придерживаются при работе с ними.
Ветки feature

Правила ветвления, слияния и именования:
- Эти ветки наследуются только от
develop; - Эти ветки должны сливаться обратно с
develop; - Имя ветки может быть любым, кроме
master,develop,release-*,hotfix-*.
В ветках feature ведется разработка функционала для будущих релизов проекта. На момент начала работы над новым функционалом, релиз в котором он появится может быть и неизвестен. Суть таких веток в том, что разработка новых функциональных возможностей может вестись параллельно разработке основной части проекта, а по завершении реализации нового функционала он может быть внедрен в одном из следующих версий, либо отклонен (в случае, если функционал не удовлетворяет целям проекта, либо по каким-либо другим причинам). Такие ветки должны создаваться в локальных репозиториях разработчиков, но ни в коем случае не в origin.
Создание ветки feature
Для создания ветки feature:
1 2 | |
При создании ветки feature ветвление осуществляется от develop.
Конец жизненного цикла ветки feature
Законченный функционал может быть объединен обратно с веткой develop для добавления его в следующий релиз:
1 2 3 4 5 6 7 8 | |
Использование флага --no-ff создает новый коммит, который содержит все изменения сливаемой ветки. Это позволяет избежать потери информации о существовании ветки, в которой велась разработка добавленного функционала. Для сравнения, взгляните на изображение ниже. Слева слияние с использованием флага --no-ff, справа без него:

Как видно, если не указать флаг –no-ff, изменения которые делались в ветке feature, невозможно будет увидеть. Это чревато тем, что для того, чтобы увидеть все изменения сделанные в ветке feature, придется перечитать все сообщения журнала, и выбирать именно те коммиты, которые касаются интересующих изменений. Отмена изменений ветки feature также становится настоящей головной болью, тогда как --no-ff упрощает эту задачу, ведь откатить придется всего лишь один коммит. Хотя именно такое поведение более предпочтительною
Для установки такого поведения слияния по умолчанию, можно заставить Git использовать --no-ff по умолчанию, путем правки .gitconfig, и добавления следующих строк:
1 2 | |
Ветки release
Правила ветвления, слияния и именования:
- Ветвление этих веток может быть произведено только от
develop; - Эти ветки должны сливаться обратно в
developи вmaster; - Имя ветки должно быть формата
release-*.
Ветки release используются при подготовке к выпуску очередного релиза. Они позволяют в самую последнюю минуту расставить все точки над i. Кроме того, они позволяют вносить исправления мелких ошибок, и подготовить метаданные для релиза (номер версии, дата создания и т.д.). Делая все эти действия в ветках release, ветка develop останется чиста, и будет готова к добавлению нового кода, отвечающего непосредственно за расширение функционала разрабатываемого продукта.
Создание ветки release производится в тот момент, когда состояние кода в develop отражает желаемое состояние нового релиза. По крайней мере, весь запланированный функционал ожидаемый в релизе, уже должен быть в develop ветке. Весь оставшийся код, отвечающий за расширение функционала, и отложенный на будущие релизы, должен ожидать, пока не будет выпущен релиз.
Именно при создании release ветки, релиз получает номер версии, а не раньше. До этого момента, в ветке develop отражены изменения запланированные для выпуска в следующем релизе, но станет ли следующий релиз 0.3 или 0.1 неизвестно до создания ветки release. Присвоение релизу номера версии происходит в момент создания release. Присвоение релизу номера версии происходит в момент создания release ветки, и следует правилам нумерации, которые предложены в проекте.
Создание ветки release
Ветки release создаются ветвлением от develop. Создание такой ветки покажем на примере. Пускай версия 1.1.5 - это текущий production релиз, и мы приближаемся к следующему большому релизу. Состояние ветки develop говорит о готовности к следующему релизу, и мы решили, что это будет 1.2, а не 1.1.6 или 2.0. Теперь, мы создаем ветку, и даем ей имя отображающее номер релиза:
1 2 3 4 5 6 7 | |
После создания новой ветки, и переключения на нее, мы изменяем везде номер версии на новый. К примеру это могут быть номера версии в исходных файлах проекта, в параметрах сборки executable файла, или еще что-то. В примере выше, это автоматизировано, и для этой цели используется вымышленный скрипт bump-version.sh, который производит нужные изменения в файлах рабочей директории, для отображения состояния новой версии. Это конечно может быть произведено и в ручную. В нашем примере, мы используем некий абстрактный проект. После этого заносим изменения в RB.
После создания новой ветки, и переключения на нее, мы изменяем везде номер версии на новый. К примеру, это могут быть номера версии в исходных файлах проекта, в параметрах сборки выполняемого файла, или еще что-то. В примере выше, это автоматизировано, и для этой цели используется вымышленный скрипт bump-version.sh, который производит нудные изменения в файлах рабочей директории, для отображения состояния новой версии. Это конечно может быть произведено и в ручную. В нашем примере, мы используем некий абстрактный проект. После этого заносим изменения в ветку release.
Этот процесс очень похож на выпуск так называемой бета-версии, когда производится только тестирование продукта, поиск и исправление ошибок, но уже никакой новый функционал не добавляется.
Закрытие ветки release и выпуск релиза
Когда состояние кода в ветке release готово к выпуску релиза, необходимо провести некоторые действия. Во-первых, нужно влить ветку release обратно в master (напомним еще раз, что каждый коммит в master - это новый релиз по определению). Далее, сделанный коммит следует отметить тегом, отображающим номер версии, что поможет в будущем легче ориентироваться в релизах. И наконец, изменения сделанные в ветке release должны быть слиты обратно в develop, так чтобы будущее релизы содержали все исправления, сделанные в процессе подготовки релиза.
Первые два шага в Git:
1 2 3 4 5 6 | |
Релиз выпущен, и помечен для будущего использования.
Для того, чтобы сохранить изменения сделаные в ветке release, нужно слить их обратно в develop:
1 2 3 4 5 | |
Этот шаг может привести к конфликтам во время слияния. Если такое произойдет, то следует исправить конфликты, и зафиксировать изменения.
Теперь релиз полностью выпущен, и release ветка может быть удалена, так как она нам больше не нужна:
1 2 | |
Ветки hotfix

Правила ветвления, слияния и именования:
- Ветвление этих веток может быть произведено только от
master; - Эти ветки должны объединяться обратно с
developиmaster; - Имя ветки должно быть формата
hotfix-*.
Ветки hotfix очень похожи на ветки release в том, что они также предназначены для подготовки к новым релизам, хотя и не запланированным. Они возникают, из-за необходимости действовать сразу же после обнаружения серьезной ошибки в production коде. Когда критическая ошибка в production версии должна быть разрешена немедленно, hotfix может быть создана ветвлением от коммита в ветке master с меткой текущей production версии.
Использование такого перехода заключается в том, что члены команды смогут дальше продолжать решение поставленных задач (в ветке develop), в то время как ответственный разработчик будет готовить исправление ошибки в production версии.
Создание ветки hotfix
Ветка hotfix создается ветвлением от ветки master. Рассмотрим небольшой пример. Допустим, версия 1.2 является текущей production версией, которая сейчас используется и вызывает проблемы из-за нескольких досадных и неприятных ошибок в коде. Изменения в ветке develop пока нестабильные, чтобы делать новый релиз. В таком случае, мы ветвлением от master создает ветку hotfix и начинаем исправлять ошибку в рамках этой ветки:
1 2 3 4 5 6 7 | |
Не забывайте применить изменения версии после ответвления!
После, исправляем ошибку и фиксируем исправление одним или несколькими коммитами.
1 2 3 | |
Закрытие ветки hotfix и выпуск внеочередного релиза
После исправления ошибки, нужно слить ветку hotfix обратно в ветку master, но также требуется объединить эту ветку обратно с develop, с тем, чтобы гарантировать, что исправления будут доступны и в последующих релизах. Эти действия аналогичны закрытию ветки release.
Обновим ветку master и тег релиза.
1 2 3 4 5 6 | |
Затем, занесем исправление в ветку develop:
1 2 3 4 5 | |
Из правила работы с ветками hotfix есть одно исключение: если ветка release уже существует, изменения ветки hotfix должны быть слиты обратно в ветку release, исключая при этом ветку develop. Слияние с веткой release приведет к тому, что после выпуска релиза, исправление будет также доступно и в ветке develop. (Если работа в develop требует немедленного исправления, и не может ждать пока будет выпущен релиз, можно безопасно занести исправление и в develop).
В конце, удаляем ветку hotfix:
1 2 | |
Подведем итоги
Предложенная модель в чем-то может показаться знакомой, и может сложиться ощущение, что автор изобрел велосипед. На самом деле, автор объединил существующие подходы, очевидные вещи в единую логичную системы и подвел черту. Данная модель не является серебрянной пулей, но она проста, наглядна и легка в применении. Модель отлично подходит для тех проектов, где используется Git. Также, данная модель помогает развить понимание ветвления и процесса выпуска релизов.