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

Теорія

Гілки в Git: паралельні версії проєкту

Що таке гілка в Git (git branch), чому робота йде в короткоживучих гілках поруч із main і як читати гілки на GitHub: перемикач гілок, ahead і behind, застарілі гілки, правила назв і графи з кількома доріжками.

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

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

Знайомся: Daniel

Знову в продуктовій команді Котокорму, і тут ти знайомишся з Daniel, одним із розробників сайту. У березні він переробив кнопку оформлення замовлення і ненадовго її зламав. Тобі цікаво, як тиждень його роботи не потрапляв на сайт, а потім раптом потрапив.

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

Що таке гілка

У березні Daniel, веброзробник Котокорму, переробляв кнопку оформлення замовлення. Тиждень він працював в окремій гілці (branch) feature/checkout-redesign, і весь цей тиждень робочий сайт лишався таким, як був. До клієнтів нічого не доходило, доки гілку не змерджили в main.

Гілка — це паралельна версія проєкту, окрема лінія комітів. Робота в ній може бути незавершена, експериментальна чи навіть зламана, а main цього не помічає. Саме це дає кільком людям працювати одночасно, не заважаючи одне одному й робочому сайту.

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

main теж гілка: основна гілка, або гілка за замовчуванням (default branch). GitHub відкриває її першою, і саме її в Котокормі викладають на сайт. У старіших репозиторіях вона часто називається master.

Тож просте правило для продакта: сайт показує main. Коміт у fix/checkout-button — справжня робота, видима на GitHub кожному, хто перемкнеться на цю гілку, але клієнти не побачать її, доки її не змерджать у main і не викладуть.

  • Так
    «Виправлення в окремій гілці; клієнти побачать його після мерджу в main».
  • Не так
    «Я бачу коміти Daniel на GitHub, отже, сайт виправлено».

    Видно в гілці — ще не означає на сайті.

Гілка — назва, що рухається

Створення гілки звучить громіздко, ніби Git копіює весь проєкт. Ні. Гілка — це просто назва, що вказує на один коміт, найновіший у своїй лінії. Коли в гілку додають коміт, назва пересувається вперед на нього. Тому гілка створюється миттєво й нічого не коштує.

Порівняй із тегом із першого модуля. Тег на кшталт v2.4.0 назавжди лишається на своєму коміті, бо версія не має змінюватися. А назва гілки рухається, доки в гілку додають коміти.

Daniel почав fix/checkout-button від останнього коміту в main, «Update FAQ answers». Відтоді кожен його коміт штовхав назву на крок далі: спершу «Reproduce bug in a test», потім «Re-enable button after cart edits».

main і гілка fix/checkout-buttonГілка починається від f02a6d4 і має два власні коміти. 8be2f70 потрапив у main уже після початку гілки.
  1. e7c3b19Add summer promo banner
  2. f02a6d4Update FAQ answers
  3. 3a9d1c4Reproduce bug in a test
  4. 8be2f70Add cat treats bundlemain
  5. b7e4a05Re-enable button after cart editsfix/checkout-button

Граф комітів, від найстарішого: main і гілка fix/checkout-button 1. коміт e7c3b19 у main "Add summer promo banner" 2. коміт f02a6d4 у main "Update FAQ answers" 3. гілка fix/checkout-button починається від коміту f02a6d4 4. коміт 3a9d1c4 у fix/checkout-button "Reproduce bug in a test" 5. коміт 8be2f70 у main "Add cat treats bundle" 6. коміт b7e4a05 у fix/checkout-button "Re-enable button after cart edits" (виділено)

У терміналі git switch -c fix/checkout-button створює гілку на поточному коміті й перемикається на неї; на GitHub гілку можна створити в перемикачі гілок, ввівши нову назву. Так чи так нічого не копіюється: з'являється нова назва, і робота починається звідти.

  • Так
    git switch -c fix/checkout-button → нова назва на поточному коміті

    Миттєво; файли не копіюються.

  • Не так
    Позначати реліз гілкою, як-от release-2.4-final

    Назва зрушить із наступним комітом. Релізи позначають тегами.

Що містить гілка

Гілка — це не лише її власні коміти. Вона несе всю історію до коміту, від якого почалася, плюс усе, що в ній додали відтоді.

Візьмімо feature/gift-wrap, яку почали від коміту m2 у main:

  • m1 «Add reviews block» і m2 «Fix rating stars» були до початку, тож у гілці вони є;
  • g1 «Add gift wrap option» і g2 «Add gift wrap price» — власні коміти гілки;
  • m3 «Update footer links» потрапив у main після початку гілки, тож у гілці його немає.

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

Коли перемикаєш гілку на feature/gift-wrap, ти бачиш в історії саме m1, m2, g1, g2. Щоб читати такий граф, простеж лінію гілки назад до місця, де вона відходить від main: усе вище цієї точки спільне, усе на відгалуженні належить гілці.

  • Так
    Простеж гілку до місця, де вона відходить від main; усе до цієї точки в гілці є.
  • Не так
    Вважати, що гілка автоматично отримує кожен новий коміт із main.

    Коміти, що потрапили в main пізніше, не з'являться в гілці, доки їх не підтягнуть.

Перемикаємо гілки на GitHub

На сторінці репозиторію натисни перемикач гілок — кнопку з написом main. Він показує список гілок, а поле пошуку фільтрує їх за назвою. Вибери fix/checkout-button — і побачиш проєкт таким, яким він є в цій гілці: файли можуть виглядати так само, але на смужці останнього коміту тепер «Re-enable button after cart edits».

Перемикання змінює лише те, що ти бачиш. Воно нічого не редагує, тож завжди безпечне.

Змінюється й адреса. Для папки в ній є /tree/<гілка>/, для файлу — /blob/<гілка>/. Тож посилання на кшталт .../blob/feature/subscription/config/prices.json показує файл із цінами в гілці feature/subscription. Якщо там доставка коштує 69 грн, це пропозиція в гілці, а не ціна, яку платять клієнти. Сайт зміниться, лише якщо цю гілку змерджать у main.

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

  • Так
    /blob/feature/subscription/ у посиланні → «у цій гілці 69 грн; на сайті й далі main»
  • Не так
    Переслати маркетингу посилання на гілку як «нову ціну доставки»

    Це пропозиція, доки її не змерджено.

Ahead і behind

Під перемикачем гілок GitHub додає підказку на кшталт «This branch is 2 commits ahead of, 1 commit behind main». Обидва числа порівнюють гілку з основною гілкою:

  • Ahead: коміти, які є в гілці, але яких немає в main. Це робота, що чекає на мердж.
  • Behind: коміти, які є в main, але яких немає в гілці. Це те, наскільки main пішла вперед без гілки.
  • Коміти до початку гілки спільні й не входять у жодне число.

Приклад: feature/subscription почалася після c2. У неї є власні s1, s2, s3, а в main відтоді з'явилися c3 і c4. Отже, 3 ahead, 2 behind.

Для продакта обидва числа щось означають. Велике ahead — велика зміна, якій знадобиться рев'ю. Велике behind — гілку будували на старій версії проєкту, тож при зустрічі з сьогоднішньою main можливі сюрпризи. Behind — не катастрофа: розробник може будь-коли підтягнути свіжу main у гілку, наприклад командою git merge main, перебуваючи в гілці. Але що довше чекати, то більше сюрпризів накопичується.

  • Так
    «12 ahead, 86 behind, оновлення 5 місяців тому: це ще в планах?»

    Числа перетворюються на продуктове питання.

  • Не так
    Рахувати спільні коміти до точки відгалуження як «ahead»

    Спільні коміти не входять у жодне число.

Сторінка гілок: активні й застарілі

Поруч із перемикачем гілок є лічильник гілок. Натисни його або View all branches у самому перемикачі — і відкриється сторінка гілок: кожна гілка з датою останнього оновлення, числами behind і ahead та її pull request, якщо він є.

Сторінка розкладає гілки по вкладках. В Active — гілки зі свіжими комітами. У Stale, тобто застарілих, — ті, куди ніхто не комітив приблизно три місяці. Тож гілка, яку востаннє чіпали 4 чи 7 місяців тому, застаріла; а та, що оновлювалася 2 дні, 3 тижні чи 2 місяці тому, ще активна.

У Котокормі цей список розповідає історію. content/delivery-copy показує, що контент-маркетологиня Emma тепер тримає тексти в репозиторії. А feature/loyalty-points — 12 ahead, 86 behind і п'ять місяців без змін: офіційно призупинена, а чесно — покинута, і змерджити її тепер було б боляче.

Гілки catchow/website
  • main

    Основна гілка. Оновлено вчора

  • feature/free-delivery-1000

    Оновлено 3 години тому · 0 behind, 3 ahead

  • fix/checkout-button

    Оновлено вчора · 1 behind, 2 ahead

  • content/delivery-copy

    Оновлено минулого тижня · 2 behind, 1 ahead

  • feature/loyalty-points

    Оновлено 5 місяців тому · 86 behind, 12 ahead

Для продакта застаріла гілка — привід спитати: ця робота ще в планах? Якщо так, гілку треба оновити, поки вона не відстала ще більше. Якщо ні, команда може видалити гілку й закрити тему, і список лишиться читабельним для всіх.

Заросла бур'яном бічна колія із запиленим покинутим вагоном лишилася далеко позаду жвавої основної лінії, де пролітають нові потяги.
Застаріла гілка — продуктове питання: чи ця робота ще в планах?
  • Так
    Спитати власника застарілої гілки, чи ця робота ще в планах
  • Не так
    Вважати кожну гілку зі списку роботою в процесі для роадмапи

    Застарілі гілки часто покинуті.

Що каже назва гілки

Назви гілок у Котокормі мають закономірність: fix/, feature/, content/. Це не правило GitHub чи Git, а звичка команди. Сам Git дозволяє майже будь-яку назву. Звичка корисна тим, що тип роботи можна вгадати ще до того, як відкриєш гілку, а список гілок читається як короткий перелік поточних задач.

Звична форма — префікс/опис, малими літерами, з дефісами замість пробілів:

  • fix/cart-total-rounding: виправлення бага;
  • feature/gift-cards: нова функціональність;
  • docs/setup-guide: документація, а не сам сайт;
  • content/delivery-copy: тексти.

Трапляються й інші стилі: номер issue на початку, як-от 142-delivery-map (робота над issue #142), або ім'я автора, як-от daniel/checkout-button. Деякі назви генерує сам GitHub: revert-97-feature/checkout-redesign створила кнопка Revert, щоб скасувати pull request #97.

Ще два префікси, hotfix/ і release/, належать до воркфлоу git flow, який у четвертому модулі порівняємо з простішим підходом Котокорму.

  • Так
    feature/gift-cards → «нова функціональність: подарункові картки»
  • Не так
    «revert-97-… — чийсь експеримент».

    Її створила кнопка Revert, щоб скасувати PR #97.

Як добре назвати гілку

Добра назва гілки одразу відповідає на два питання: що це за робота і про що вона. Префікс дає тип (fix/, feature/, content/, docs/), а кілька слів після нього — тему.

Опис має бути коротким і конкретним: fix/promo-code-spaces, а не fix/bug чи fix/the-promo-code-field-on-checkout-rejects-codes-with-spaces. Малі літери й дефіси; пробіли в назвах гілок Git узагалі не дозволяє, а великі літери на деяких комп'ютерах плутають.

Префікс вибирають за природою зміни, а не за важливістю. Баг у наявному полі — це fix, навіть якщо він терміновий; нова опція для клієнтів — feature. Якщо команда пов'язує гілки з issues, номер іде першим: 151-promo-code-spaces.

Чому це важливо продакту? Назви гілок трапляються в pull requests, на сторінці гілок і в посиланнях, які тобі надсилають. Коли вони тримаються однієї звички, можна проглянути список із двадцяти гілок і зрозуміти, що зараз у роботі, нічого не відкриваючи.

  • Так
    fix/promo-code-spaces

    Тип плюс тема, малі літери, дефіси.

  • Не так
    daniel-stuff

    Каже хто, а не що.

  • Не так
    feature/promo-code-spaces для бага

    Префікс має відповідати типу роботи.

Коротке життя і тихий кінець

Здорові гілки живуть дні, а не місяці. Одна гілка — одна задача: почати від свіжої main, зробити кілька комітів, відкрити pull request, змерджити, видалити.

Здорова гілка починається від свіжої main, отримує кілька комітів, проходить pull request, мерджиться й видаляється: назва зникає, робота лишається в main.

Слово «видалити» лякає після всієї роботи, але видалення гілки прибирає назву, а не роботу. Щойно зміни змерджено, вони живуть в історії main. Назва гілки була лише вказівником, і свою задачу вона виконала.

Робітник висмикує невеликий вказівник там, де бічна стежка вже влилася в широку бруковану дорогу; дорога тягнеться далі неушкоджена.

Після мерджу GitHub пропонує на сторінці pull request кнопку Delete branch. Якщо гілка комусь знову знадобиться, на тій самій сторінці є Restore branch. Багато команд вмикають у налаштуваннях репозиторію автоматичне видалення змерджених гілок, як і Котокорм; інакше список гілок перетворюється на горище. Розробники роблять те саме в терміналі через git branch -d fix/footer-links, і ця команда відмовляється видаляти гілку, чию роботу ще не змерджено.

Короткі гілки ще й безпечніші. Що менше часу гілка проводить окремо від main, то менше сюрпризів, коли вона повертається, і то менший pull request комусь доведеться переглядати.

  • Так
    Змерджити pull request, а тоді Delete branch

    Робота лишається в main; список — охайним.

  • Не так
    Тримати одну гілку відкритою місяцями й додавати туди кожну нову задачу

    Вона відстає від main, і мерджити її стає боляче.

Чому на «behind» варто глянути

Ось як Daniel зламав оформлення замовлення в березні. Він відгалузився від main, зробив три коміти й усе перевірив у своїй гілці. Тим часом у main змерджили панель із повідомленням про cookie від колеги. Його гілка відставала на один коміт, а він і не глянув.

Березень: редизайн кнопки перед мерджемГілка на 3 коміти ahead від main і на 1 behind: у ній немає панелі з повідомленням про cookie.
  1. 4d2a8e1Add order tracking link
  2. 61b9f3cNew checkout button styles
  3. d18e6a0Add cookie notice bar (#95)main
  4. 92c4b1fMove button to sticky footer
  5. 0a7f5e3Update button textsfeature/checkout-redesign

Граф комітів, від найстарішого: Березень: редизайн кнопки перед мерджем 1. коміт 4d2a8e1 у main "Add order tracking link" 2. гілка feature/checkout-redesign починається від коміту 4d2a8e1 3. коміт 61b9f3c у feature/checkout-redesign "New checkout button styles" 4. коміт d18e6a0 у main "Add cookie notice bar (#95)" (виділено) 5. коміт 92c4b1f у feature/checkout-redesign "Move button to sticky footer" 6. коміт 0a7f5e3 у feature/checkout-redesign "Update button texts"

Тож він перевіряв версію сайту з новою кнопкою, але без панелі cookie. Після мерджу в main уперше опинилося і те, і те. На телефонах панель лягла просто на кнопку, натиснути її не міг ніхто, і за десять хвилин команда відкотила мердж.

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

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

  • Так
    «1 behind? Підтягни свіжу main і перевір на телефоні перед мерджем».
  • Не так
    «Git не скаржився, отже, мердж безпечний».

    Git ловить лише зіткнення в тих самих рядках.

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

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

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