Перейти до змісту
Увійти

Теорія

Git flow: гілки develop, release і hotfix

Модель гілок, зорієнтована на релізи: довгоживучі main і develop, гілки feature, release-гілка для кожної версії, гілки hotfix і теги на main. Коли вона доречна (релізи через магазини застосунків, кілька підтримуваних версій), чому багато вебкоманд перейшли на GitHub flow чи trunk-based development і як упізнати модель за списком гілок.

~10 хв читання · 11 вправ · 250 балів

Перед початком

«Справжні релізи»

Новий тиждень у продуктовій команді Котокорму. Банер осіннього розпродажу вийшов у понеділок точно вчасно, а потім за обідом CEO спитав, чому в Котокормі немає «справжніх релізів, як у будь-якої серйозної компанії». Daniel колись саме так працював над мобільним застосунком.

У цьому уроці ти познайомишся з моделлю гілок, побудованою навколо релізів, побачиш, коли вона доречна і чому багато вебкоманд від неї відмовилися, і навчишся впізнавати модель команди за списком її гілок.

Git flow: дві довгоживучі гілки й три види тимчасових

Git flow це модель гілок, зорієнтована на релізи, з 2010 року. Замість однієї main і коротких гілок у ній дві гілки, що живуть завжди, і три види тимчасових:

  • main: лише випущені версії. Кожен коміт у ній отримує тег версії, як-от v2.7.0 чи v2.8.0. В оригінальному описі цю гілку називали master.
  • develop: тут із готових фіч збирають наступну версію.
  • feature/*: по одній фічі, відгалужується від develop і повертається в неї: git switch -c feature/workout-reminders develop.
  • release/*: готує одну версію до випуску; лише виправлення й зміна номера версії.
  • hotfix/*: термінове виправлення версії, яку користувачі вже мають; відгалужується від main.

Команди з git flow зазвичай зливають через мердж-коміт, а не squash (git merge --no-ff feature/workout-reminders), щоб кожна фіча лишалася видимою в develop як окрема петля. Так радить і оригінальна модель.

На графі це п'ять доріжок із мерджами в обидва боки, тоді як у GitHub flow одна доріжка й короткі бічні гілки.

Git flow у команді мобільного застосункуУ main лише випущені версії, кожна з тегом. develop збирає наступний реліз. Фічі відгалужуються від develop і повертаються в неї; релізну гілку відрізають від develop і мерджать у main та назад у develop; хотфікс починається від main і потрапляє в обидві.
  1. 2c71a0eVersion 2.7.0v2.7.0
  2. 4d8e2b1Update translations
  3. 7a3f9c2Add reminder settings
  4. c5e0d38Fix crash on loginhotfix/2.7.1
  5. e8b2f14Merge hotfix/2.7.1v2.7.1
  6. 0f6c3a9Merge hotfix/2.7.1 into develop
  7. 91be4d7Send reminder notificationsfeature/workout-reminders
  8. 5b9d7e2Merge feature/workout-reminders
  9. a4c1e86Bump version to 2.8.0
  10. 3e7f0b5Fix reminder text on small screensrelease/2.8.0
  11. 6d2a9f3Merge release/2.8.0mainv2.8.0
  12. b8e4c71Merge release/2.8.0 into developdevelop

Граф комітів, від найстарішого: Git flow у команді мобільного застосунку 1. коміт 2c71a0e у main "Version 2.7.0", тег v2.7.0 2. гілка develop починається від коміту 2c71a0e 3. коміт 4d8e2b1 у develop "Update translations" 4. гілка feature/workout-reminders починається від коміту 4d8e2b1 5. коміт 7a3f9c2 у feature/workout-reminders "Add reminder settings" 6. гілка hotfix/2.7.1 починається від коміту 2c71a0e 7. коміт c5e0d38 у hotfix/2.7.1 "Fix crash on login" (виділено) 8. мердж-коміт e8b2f14: hotfix/2.7.1 змерджено в main "Merge hotfix/2.7.1", тег v2.7.1 9. мердж-коміт 0f6c3a9: hotfix/2.7.1 змерджено в develop "Merge hotfix/2.7.1 into develop" 10. коміт 91be4d7 у feature/workout-reminders "Send reminder notifications" 11. мердж-коміт 5b9d7e2: feature/workout-reminders змерджено в develop "Merge feature/workout-reminders" 12. гілка release/2.8.0 починається від коміту 5b9d7e2 13. коміт a4c1e86 у release/2.8.0 "Bump version to 2.8.0" 14. коміт 3e7f0b5 у release/2.8.0 "Fix reminder text on small screens" 15. мердж-коміт 6d2a9f3: release/2.8.0 змерджено в main "Merge release/2.8.0", тег v2.8.0 16. мердж-коміт b8e4c71: release/2.8.0 змерджено в develop "Merge release/2.8.0 into develop"

  • Так
    feature/workout-reminders від develop, назад у develop через мердж-коміт.

    Фічі живуть на боці develop.

  • Не так
    Гілка фічі від main, змерджена просто в main.

    У git flow main приймає лише релізи й хотфікси.

Змерджено не означає випущено

У git flow фічу зливають у develop, а не в main. Для PM це головна відмінність: змерджено означає «буде в наступній версії», а не «користувачі вже мають».

Уяви develop як перон, а реліз як потяг, що відходить за розкладом. Нагадування про тренування змерджили посеред спринту, а на телефони людей вони потрапили майже через два тижні: треба було відгалузити релізну гілку, протестувати її, злити в main, поставити тег, пройти перевірку магазинів застосунків, а тоді кожен користувач мав оновити застосунок.

У git flow фіча, змерджена в develop, чекає на відрізання релізу, тиждень тестування й перевірку в магазині; користувачі бачать її лише після оновлення, майже через два тижні.

У GitHub flow Котокорму змерджено означає «на сайті за кілька хвилин». У git flow корисне питання не «чи змерджено?», а «у якому релізі?». Команди, які про це забувають, отримують маркетинг, що анонсує фічі, яких користувачі ще не бачать.

GitHub показує різницю, якщо читати уважно. Шапка pull request каже, що його змерджено «into develop», а найновіший тег у репозиторії досі попередня версія. git tag --contains <commit> нічого не виводить, доки не поставлять тег релізу, який містить цей коміт.

  • Так
    «Нагадування змерджено в develop, тож вони вийдуть у 2.8.0».

    Відповідає на «у якому релізі?».

  • Не так
    «Нагадування змерджено, користувачі можуть спробувати їх уже ввечері».

    Змерджено в develop не означає випущено.

Статус-апдейти, коли працюєш із релізними потягами

Коли команда працює в git flow, кожен статус-апдейт про фічу потребує двох фактів: у якій версії вона і коли ця версія дійде до користувачів. Саме слово «змерджено» найоманливіше, що можна надіслати маркетингу.

Добрий апдейт називає етап і наступну дату:

  • «Нагадування в develop: вони вийдуть у 2.8.0. Релізну гілку відгалузять у понеділок, далі тиждень тестів і перевірка в магазинах; користувачі отримають їх приблизно за два тижні, після оновлення».
  • «Цілі кроків не встигли до відгалуження 2.8.0; їх планують на 2.9.0».
  • «2.8.0 схвалено в магазинах, і вона поступово розгортається».

Магазини застосунків додають власний етап: версію можуть схвалити, але розгортати поступово на дедалі більшу частку користувачів. І змін у мобільному застосунку ніхто не побачить, доки його не оновить.

Тож плануй анонси на день, коли версія справді доступна, а не на день мерджу фічі. Обіцяй дату лише після того, як реліз подано, і підтверджуй її ще раз, коли його схвалять.

Готова посилка разом з іншими чекає на складі наступної вантажівки, а в іншому кінці міста покупці чекають біля дверей.
  • Так
    «У 2.8.0, яка дійде до користувачів приблизно за два тижні. Підтверджу дату, щойно реліз подадуть».

    Версія, терміни й обіцянка підтвердити.

  • Не так
    «Змерджено, анонсуйте в п'ятницю».

    Користувачі отримають це лише через два тижні.

Відгалуження релізної гілки

Релізна гілка (release branch) це місце, де одна версія готується до випуску. Коли в develop набирається досить для наступної версії, її відгалужують: git switch -c release/2.8.0 develop. Відтоді це окрема доріжка.

У релізній гілці команда лише виправляє баги, змінює номер версії й пише нотатки до релізу. Жодних нових фіч. Тестувальники тиждень чи близько того ганяють її на всі лади.

Тим часом develop лишається відкритою, і фічі для 2.9.0 надходять далі. У релізній гілці їх немає: у 2.8.0 потрапляє лише те, що було в develop у мить відгалуження, плюс виправлення, зроблені в самій релізній гілці.

Коли реліз готовий, його зливають у main і ставлять тег (git tag -a v2.8.0 -m "Version 2.8.0"), а тоді зливають назад у develop, щоб виправлення, зроблені під час стабілізації, не загубилися.

Щоб відповісти «що є в 2.8.0?», знайди коміт, від якого відгалузили релізну гілку, і додай виправлення в ній. Порядок мерджів у develop не важить; важить момент відгалуження.

  • Так
    «Темна тема була в develop на момент відгалуження, тож вона в 2.8.0; цілі кроків прийшли пізніше».

    Читає точку відгалуження.

  • Не так
    «Обидві змерджили до тегу v2.8.0, отже, обидві в ньому».

    Важить відгалуження, а не дата тегу.

Дата відгалуження: коли спізнився на потяг

У git flow дата відгалуження, день, коли від develop створюють релізну гілку, найважливіша дата в календарі. Фіча, змерджена в develop наступного дня після відгалуження, спізнилася на потяг: її немає в релізній гілці, тож вона чекає на наступну версію.

Потяг рушає зі станції, а людина з посилкою вибігає на перон надто пізно; наступний потяг видно далеко на колії.
Не встигли до дати відгалуження — фіча поїде наступним релізом.

Це дає плануванню чіткий ритм: дата відгалуження, тиждень стабілізації, дата релізу. Роадмапи в таких командах часто читаються за версіями: «2.8.0: нагадування, 2.9.0: цілі кроків». Для календаря це передбачувано, і PM часто завзято домовляються, що встигне до відгалуження.

Ціна це очікування. Навіть покращення в один рядок, змерджене в develop, їде наступним потягом, через два тижні чи місяць. Розклад обходить лише щось настільки термінове, що заслуговує на хотфікс.

Для PM звідси дві звички. Знай наступну дату відгалуження й заздалегідь скажи команді, які фічі мусять встигнути. А коли щось не встигло, скажи про це прямо й перенеси на наступну версію, а не проштовхуй фічу в релізну гілку, яка має приймати лише виправлення.

  • Так
    «Цілі кроків не встигли до понеділкового відгалуження; вони в 2.9.0, через місяць».

    Чесно про потяг, яким вона поїде.

  • Не так
    Злити нову фічу просто в release/2.8.0, щоб утиснути її.

    Релізні гілки приймають лише виправлення.

Гілки хотфіксів

Хотфікс це термінове виправлення того, що користувачі вже мають, між релізами. Коли версія 2.7.0 у частини користувачів падає під час входу, ніхто не чекає два тижні на 2.8.0.

Хотфікс починається з того, що в користувачів, а не з develop, повної невипущеної роботи. У git flow це main на тезі релізу:

Механік латає колесо авто, що вже їде дорогою з водієм, а позаду в гаражі на підйомниках стоять недороблені нові машини.
Хотфікс починають від того, що вже мають користувачі, а потім повертають у поточну роботу.
git switch -c hotfix/2.7.1 v2.7.0

Далі кроки:

  1. Закомітити лише виправлення крашу й пройти рев'ю.
  2. Злити хотфікс у main і поставити тег v2.7.1.
  3. Злити хотфікс і в develop, щоб наступні релізи зберегли виправлення.
  4. Випустити 2.7.1, для мобільного застосунку подати в магазини. Тег позначає саме те, що випускають, тож подання йде після тегу.

Нічого іншого в гілку хотфіксу не йде: жодних дрібних покращень «заодно». Що вужче виправлення, то швидше його протестувати й схвалити і то менший шанс зламати щось іще для користувачів, яким і так важко.

  • Так
    git switch -c hotfix/2.7.1 v2.7.0

    Починається саме з того, що в користувачів.

  • Не так
    Відгалузити хотфікс від develop, бо вона новіша.

    Разом із виправленням вийдуть невипущені фічі.

Злиття назад: не дай багу повернутися

У хотфіксу два пункти призначення. Перший це main із новим тегом, щоб користувачі отримали виправлення. Другий це місце, де будують наступну версію, щоб вона зберегла виправлення.

Зазвичай це develop. Але якщо в цей момент відкрита релізна гілка, скажімо release/2.8.0, хотфікс іде в релізну гілку замість develop. А в develop виправлення потрапить, коли реліз зіллють назад. Якщо злити його лише в develop, воно оминуло б 2.8.0, яку відгалузили ще до виправлення.

Чому це важливо, хоча користувачі вже мають 2.7.1: версію 2.8.0 збирають із develop і її релізної гілки, а не з main. Забудеш злиття назад, і 2.8.0 вийде з тим самим крашем під час входу. Користувачі, які щойно отримали виправлення, втратять його з наступним оновленням, і ця регресія ніким не запланована.

Для PM після кожного хотфіксу варто одне питання: «чи є виправлення в наступному релізі?» Командою git branch --contains <commit> інженер перевірить, які гілки вже містять коміт із виправленням.

  • Так
    Злити hotfix/2.7.1 у main і у відкриту release/2.8.0.

    2.8.0 зберігає виправлення.

  • Не так
    Злити хотфікс лише в main, бо користувачі вже мають виправлення.

    Наступний реліз поверне краш.

Коли git flow пасує, а коли ні

Git flow пасує, коли релізи це події:

  • мобільний застосунок, що проходить перевірку в магазинах, перш ніж його можна завантажити;
  • програма, яку клієнти встановлюють і оновлюють, коли захочуть;
  • кілька версій, які підтримують одночасно, як-от коли клієнти досі користуються двома попередніми релізами десктопної програми.

Там релізні гілки дають стабільне місце для тестування однієї версії, а гілки хотфіксів лагодять саме ту версію, що в користувачів.

Для сайту з однією живою версією, як магазин Котокорму чи маркетинговий сайт, develop здебільшого додає очікування й подвійні мерджі. Багато вебкоманд перейшли на GitHub flow або на trunk-based development: усі зливають малі зміни в main щонайменше щодня, гілки живуть години, а не дні, а фіча-флаги ховають усе незавершене. Trunk-based вимагає дуже добрих автоматичних тестів і чималої дисципліни.

Пізніше навіть автор моделі додав примітку про те саме: вебзастосункам, які викладають безперервно, краще підходить простіший процес, а git flow для програм із явними версіями. Обирай за тим, як ви випускаєте, а не за тим, що звучить солідно.

  • Так
    Десктопна програма з щомісячними релізами й двома старшими версіями в ужитку: git flow.

    Релізи це події, живуть кілька версій.

  • Не так
    Додати develop і щомісячні релізи магазину, який викладає кожен мердж за хвилини.

    Додає очікування й подвійні мерджі задарма.

Як упізнати модель за списком гілок

Усі три моделі трапляться в інших репозиторіях: застосунок партнера, агенція, наступна робота. Сторінка гілок видає модель за секунди; у терміналі ті самі віддалені гілки показує git branch -r.

  • Git flow: develop поруч із main, назви на кшталт release/3.2.0 і hotfix/3.1.2 і кілька гілок feature/…. Часто develop навіть основна гілка (default), тож нові pull requests автоматично йдуть у неї.
  • GitHub flow: лише main і кілька свіжих гілок fix/…, feature/… чи content/…, кожна віком кілька днів і з pull request у main.
  • Trunk-based development: майже нічого, крім main, гілки живуть години, а незавершену роботу ховають численні фіча-флаги.
Гілки репозиторію фітнес-застосунку
  • develop

    Default (основна) · оновлено сьогодні

  • main

    Оновлено 9 днів тому · тег v2.8.0

  • release/2.9.0

    Оновлено вчора · на 3 коміти попереду develop

  • feature/step-goals

    Оновлено 2 години тому · pull request у develop

  • hotfix/2.8.1

    Оновлено годину тому · pull request у main

Є ще дві підказки. Сторінка тегів показує, чи команда взагалі нумерує версії: рівний ряд v3.1.0, v3.1.1, v3.2.0 пасує до git flow. А список pull requests показує, у яку базову гілку йде більшість із них.

Знаючи модель, ти розумієш, що в цій команді означає «змерджено», ще до того, як комусь пообіцяєш дату.

  • Так
    «develop основна гілка, і є release/3.2.0: змерджено тут означає наступний реліз».

    Спершу модель, тоді значення «змерджено».

  • Не так
    Вважати, що кожна команда викладає після мерджу, як Котокорм.

    «Змерджено» в різних моделях означає різне.

Готові потренуватися?

Виконайте вправи уроку й отримайте 250 балів, коли пройдете його вперше.

Розпочати вправи