Конспект «A successful Git branching model» Vincent Driessen (2010): master и develop живут вечно, feature / release / hotfix — временные, каждый merge — с --no-ff, каждый release получает tag.
Модель держится на двух вечных branch — master (всегда production-ready) и develop (интеграция изменений для следующего release) — плюс три типа временных branch: feature, release и hotfix, у каждого строго определено, от чего он branch-ится и куда merge-ится. Каждый merge в master — по определению новый production release и помечается tag. Все merges выполняются с --no-ff, чтобы история каждого feature оставалась сгруппированной и её можно было целиком прочитать или revert-нуть.
master и developОба существуют в центральном репозитории (origin) с бесконечным временем жизни:
origin/master |
HEAD всегда отражает production-ready состояние. Каждый merge в master — «новый production release по определению»: правило настолько строгое, что на commit в master можно повесить hook и автоматически deploy-ить в production. |
origin/develop |
Отражает «последние доставленные изменения для следующего release» — integration branch, источник nightly builds. Когда develop достигает стабильного состояния, изменения merge-ятся в master и помечаются tag с номером release. |
Все три — обычные git branches с ограниченным временем жизни; никакой магии в них нет, «особыми» их делают только конвенции использования: от чего branch-иться, куда merge-иться и как называться.
Назначение: разработка новых features для ближайшего или будущего release. Живёт, пока feature в разработке; если эксперимент не удался — branch просто выбрасывается. Обычно существует только в репозиториях разработчиков, а не в origin.
| Branch от | develop |
| Merge в | develop |
| Naming | что угодно, кроме master, develop, release-*, hotfix-* |
Назначение: подготовка production release — последняя полировка, мелкие bugfixes, метаданные (номер версии, build dates). Пока release готовится, develop свободен для features следующего release. Номер версии присваивается только в момент создания release branch — до этого develop отражает «следующий release», но неизвестно, станет ли он 0.3 или 1.0. Крупные новые features здесь запрещены.
| Branch от | develop |
| Merge в | develop и master |
| Naming | release-* |
Назначение: незапланированный production release — критичный bug в живой версии надо чинить немедленно. Branch создаётся от tag на master, помечающего сломанную production-версию. Смысл: пока один человек готовит быстрый production fix, работа остальной команды на develop продолжается.
| Branch от | master |
| Merge в | develop и master |
| Naming | hotfix-* |
исключение Если в этот момент существует release branch, hotfix merge-ится в него, а не в develop: fix попадёт в develop вместе с завершением release branch. Если fix нужен в develop срочно — можно merge-ить и туда сразу.
| Branch type | Branches from | Merges into | Naming |
|---|---|---|---|
feature | develop | develop | всё, кроме master, develop, release-*, hotfix-* |
release | develop | develop и master | release-* |
hotfix | master | develop и master | hotfix-* |
# создать feature branch от develop
$ git checkout -b myfeature develop
Switched to a new branch "myfeature"
# закончить: merge в develop с --no-ff, удалить branch, push
$ 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
# создать release branch от develop; здесь версия получает номер
$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2 # bump-version.sh — вымышленный script; суть — файлы меняются
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(-)
# выпустить: merge в master + 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 # при желании подписать: -s или -u <key>
# и обязательно merge обратно в develop, потом удалить branch
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2 # скорее всего конфликт на номере версии — исправить и commit
Merge made by recursive.
(Summary of changes)
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
# создать hotfix branch от master
$ 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(-)
# починить bug, commit-нуть fix
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)
# выпустить fix: merge в master + tag, затем merge в develop
$ git checkout master
$ git merge --no-ff hotfix-1.2.1
$ git tag -a 1.2.1
$ git checkout develop
$ git merge --no-ff hotfix-1.2.1
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
--no-ff--no-ff заставляет git создать merge commit даже там, где возможен fast-forward. Аргументы из статьи:
--no-ff по истории не видно, какие commits вместе реализовали feature — пришлось бы вручную читать все log messages.merge --no-ff: commits feature — группой, merge commit виденmaster и develop; feature уходит из develop и возвращается в него; release готовит версию и даёт tag 1.0 на master. Все стрелки-merge — с --no-ff.note of reflection (2020)
Спустя десять лет автор дописал к статье оговорку: модель родилась в 2010 году, git-flow стал «hugely popular», но, к сожалению, многие стали относиться к нему как к догме или panacea. За это время разработка сместилась к web-приложениям, которые «typically continuously delivered, not rolled back» и не существуют в нескольких поддерживаемых версиях одновременно — это не тот класс software, под который модель проектировалась.
Командам с continuous delivery автор сам советует не втискивать git-flow, а взять «a much simpler workflow (like GitHub flow)». Git-flow остаётся уместным для explicitly versioned software и продуктов, где надо поддерживать несколько версий «in the wild». Финальный совет: «panaceas don't exist. Consider your own context. … Decide for yourself».