Коллективная работа над сложным проектом (в декабре мы выпустили первый релиз SkyCover Infrastructure CD-ROM) породила очень много потребностей: надо выпускать релизы, добавлять функционал, делать хотфиксы. Захотелось иметь автоматические nightly-builds…
Казалось бы, для пользователей git и github.com (каковыми мы являемся), эти возможности находятся «на кончиках пальцев» и все должно быть просто. Однако git — лишь инструмент и для достижения хорошего результата требуется следование определенной методологии.
Эта статья — вольное изложение хорошо известного первоисточника. Желающие почитать подробный перевод могут погуглить «удачная модель ветвления git». К сожалению, попробовав читать пару переводов я впал в дремоту и вернулся к англоязычному оригиналу. Поэтому ниже я попробую свои силы в изложении только сути дела :)
Основные ветки
Центральный репозиторий содержит две ветки с бесконечным сроком жизни:
origin/master
— всегда содержит исходник, готовый к промышленному использованию.origin/develop
— в нее добавляются изменения, которые войдут в следующий релиз. Иногда эту ветку называют «integration branch», потому что в ней объединяются изменения разных тематик и от разных разработчиков. Именно изdevelop
делают ночные сборки (nightly builds).
В общем случае, develop
— это не место для разработки, а готовый код, который точно войдет в следующий релиз.
Когда develop
достигает стабильного состояния, она сливается в master и помечается тэгом номера релиза (git tag
) .
Условимся, что изменения, которые в какой-то момент сливаются (мержатся) в
master,
по определению становятся новым релизом.
При достаточной педантичности любой коммит в master будет основанием для изготовления нового дистрибутива или рассылки изменения на работающие системы. Иногда это можно даже автоматизировать с помощью git hook
.
Вспомогательные ветки
Остальные ветки создаются по необходимости и имеют ограниченный срок жизни.
Тем не менее, работа с ними подчиняется определенным правилам и можно выделить три типа веток:
- Feature branches (новые возможности)
- Release branches (подготовка релиза)
- Hotfix branches (исправления)
Feature branches — новые возможности
Ответвляется от: develop
Мержится в: develop
Название: любое, кроме master, develop, release-*,
или hotfix-*
Ветки новых возможностей (иногда называют «тематические ветки», topic branches) используются для разработки нового функционала. Заранее нельзя сказать, в какой именно релиз войдут эти изменения, поэтому ветка живет до тех пор, пока она не вольется обратно в develop или ее разработка не будет отменена. После этого ветка удаляется.
Тематические ветки обычно находятся в локальных репозиториях разработчиков и не хранятся в origin.
Создание feature branch
$ git checkout -b myfeature develop Switched to a new branch "myfeature"
Слияние feature branch с develop
$ git checkout develop Switched to branch 'develop' $ git merge --no-ff myfeature Updating ea1b82a..05e9557 (Summary of changes) $ git branch -d myfeature Deleted branch myfeature (was 05e9557). $ git push origin develop
Флаг --no-ff
используется для того чтобы выделить в истории develop
участок, относящийся к конкретному слиянию. Сравните:
Во втором случае из истории коммитов будет непонятно, которые из них относятся к добавленному в код функционалу. Выяснить это можно будет только внимательным чтением журнала и полностью откатить функционал будет тоже непросто.
Release branches — ветки релизов
Ответвляется от: develop
Мержится в: develop
и master
Название: release-*
Ветки релизов предназначены для подготовки кода к очередному релизу. В ветке релиза можно спокойно подчистить все хвосты, провести тестирование, а ветку develop в это же время освободить для продолжения разработки функционала.
Ветка релиза создается в тот момент, когда develop
максимально соответствует требуемому состоянию релиза. Это значит, что все изменения, назначенные в этот релиз должны быть уже слиты в develop,
а все изменения, предназначнные для последующих релизов должны ждать, пока ветка релиза не будет создана.
Именно в начале ветки релиза, можно определить новую версию продукта, но не раньше. С этого момента ветка develop станет отражать состояние «следующего релиза», но будет ли это скажем, версия «0.3» или «1.0», и что именно в нее войдет — пока не ясно и станет окончательно ясно только при следующей процедуре релиза.
Создание ветки релиза
Ветки релиза создаются из develop. Например, скажем, версия 1.1.5 — это текущий релиз и мы готовим следующий большой релиз. Состояние develop готово для релиза и мы решили дать ему номер версии 1.2 (а не 1.1.6 или 2.0):
$ git checkout -b release-1.2 develop Switched to a new branch "release-1.2" $ ./bump-version.sh 1.2 Files modified successfully, version bumped to 1.2. $ git commit -a -m "Bumped version number to 1.2" [release-1.2 74d9424] Bumped version number to 1.2 1 files changed, 1 insertions(+), 1 deletions(-)
bump-version.sh — это некий скрипт для изменения номера версии в файлах релиза. Можете просто подправить руками ;)
Созданная ветка будет существовать в репозитории до момента публикации релиза.
В это время все исправления должны сливаться именно в ветку релиза, а не в develop.
А новые функции, напротив, должны сливаться в develop
и ждать следующего релиза.
Завершение ветки релиза
Когда релиз готов, он, в первую очередь, сливается в master (помните, мы условились, что это и есть непосредственно релиз).
Далее этому коммиту в master должен быть назначен тэг (git tag
) для удобства последующего обращения.
$ git checkout master Switched to branch 'master' $ git merge --no-ff release-1.2 Merge made by recursive. (Summary of changes) $ git tag -a 1.2
И, наконец, изменения master должны быть слиты обратно в develop,
чтобы отразить все исправления, сделанные во время релиза.
$ git checkout develop Switched to branch 'develop' $ git merge --no-ff release-1.2 Merge made by recursive. (Summary of changes)
Это последнее действие может привести к конфликту. В этом случае внесите исправления и выполните коммит.
Теперь все готово и мы можем удалить ненужную ветку релиза:
$ git branch -d release-1.2 Deleted branch release-1.2 (was ff452fe).
Примечание: Чтобы убрать ветку из удаленного репозитория, например, на github, выполните также:
$ git push origin --delete release-1.2
Hotfix branches — исправления
Ответвляется от: master
Мержится в: develop
и master
Название: hotfix-*
Ветки исправлений похожи на ветки релизов, в том плане, что они служат для подготовки нового релиза. Только они незапланированные и возникают в тот момент, когда критическая ошибка в релизе требует исправления.
Для этого ветка исправления ответвляется от тэга соответствующего релиза на ветке master.
Далее работа над исправлением продолжается в ветке исправления, а разработка продукта идет своим чередом в ветке develop.
Создание ветки исправления
Ветки исправления создаются из master. Например, скажем, версия 1.2 — это текущий релиз и он имеет проблемы. Однако изменения в develop пока что нестабильны. Мы можем создать ветку для исправления:
$ git checkout -b hotfix-1.2.1 master Switched to a new branch "hotfix-1.2.1" $ ./bump-version.sh 1.2.1 Files modified successfully, version bumped to 1.2.1. $ git commit -a -m "Bumped version number to 1.2.1" [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1 1 files changed, 1 insertions(+), 1 deletions(-)
Не забудьте поменять номер версии!
Теперь внесем исправления для решения проблемы:
$ git commit -m "Fixed severe production problem" [hotfix-1.2.1 abbe5d6] Fixed severe production problem 5 files changed, 32 insertions(+), 17 deletions(-)
Завершаем ветку исправления
$ git checkout master Switched to branch 'master' $ git merge --no-ff hotfix-1.2.1 Merge made by recursive. (Summary of changes) $ git tag -a 1.2.1
$ git checkout develop Switched to branch 'develop' $ git merge --no-ff hotfix-1.2.1 Merge made by recursive. (Summary of changes)
Тут есть одно исключение: если ветка соответствующего релиза еще существует, то изменения надо сливать в нее, а не в develop.
Наконец, удалим временную ветку:
$ git branch -d hotfix-1.2.1 Deleted branch hotfix-1.2.1 (was abbe5d6).
В завершение
На этом рисунке показана полная картина происходящего.
В высоком разрешении ее можно взять по адресу оригинала http://nvie.com/posts/a-successful-git-branching-model/
Автор оригинала разработал утилиты git-flow для упрощения описанного процесса. Почитать можно здесь http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/
или, в моем пересказе, здесь: Почему вы не используете git-flow?