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

Теорія

Релізи на GitHub і версії: теги, SemVer, changelog

Що таке тег Git, як читати номер версії на кшталт v1.4.1 (SemVer простими словами), як працюють релізи на GitHub і згенеровані release notes, чим вони відрізняються від changelog, як перевірити, чи є виправлення у версії, і чому «змерджено», «випущено» і «доступно всім» можуть бути трьома різними моментами.

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

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

У якій версії виправлення?

Знову в продуктовій команді Котокорму, і підтримка має прохання щодо виправлення кнопки: вони хочуть писати клієнтам «виправлено у версії така-то», як у примітках до оновлень застосунку. Чесна відповідь поки що: #134 не входить у жодну версію.

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

Тег: незмінна назва для одного коміту

Тег (tag) це незмінна назва для одного коміту. Гілка рухається вперед із кожним новим комітом, а тег лишається саме там, де його поставили. Тому версії і є тегами: v1.3.0 має означати той самий код і завтра, і за рік.

Створити тег можна двома командами:

git tag -a v1.3.0 -m "Cat treats bundle"
git push origin v1.3.0

-a створює анотований тег із власним автором, датою й повідомленням; звичайний пуш теги не надсилає, тож друга команда відправляє його на GitHub.

У Котокорму v1.3.0 позначає 8be2f70, набір смаколиків для котів. Відтоді main пішла на п'ять комітів уперед, зокрема з виправленням кнопки #134 (d51c7e8). Тож #134 на сайті з моменту свого деплою, але ні в якій версії ще немає: жоден тег не вказує на коміт, який його містить.

main і її єдиний свіжий тегv1.3.0 позначає 8be2f70. Усе, що змерджили після нього, зокрема #134, уже на сайті, але не входить у жодну версію.
  1. 8be2f70Add cat treats bundlev1.3.0
  2. 6f2c8a1Raise free-delivery threshold to 1000 UAH (#133)
  3. a7d93e4Rewrite delivery page copy (#132)
  4. d51c7e8Fix checkout button after cart edits (#134)
  5. 3b8e1f6Add autumn sale banner (#136)
  6. 9c4a2d7Add loyalty points behind a flag (#155)main

Граф комітів, від найстарішого: main і її єдиний свіжий тег 1. коміт 8be2f70 у main "Add cat treats bundle", тег v1.3.0 2. коміт 6f2c8a1 у main "Raise free-delivery threshold to 1000 UAH (#133)" 3. коміт a7d93e4 у main "Rewrite delivery page copy (#132)" 4. коміт d51c7e8 у main "Fix checkout button after cart edits (#134)" (виділено) 5. коміт 3b8e1f6 у main "Add autumn sale banner (#136)" 6. коміт 9c4a2d7 у main "Add loyalty points behind a flag (#155)"

Які теги містять коміт, показує git tag --contains d51c7e8; порожня відповідь означає «ще не випущено в жодній версії». Тег схожий на закладку, приколоту до однієї сторінки, поки в кінці зошита з'являються нові.

  • Так
    «#134 на сайті, але ще не в жодній версії: v1.3.0 вказує на раніший коміт».

    Відділяє «на сайті» від «випущено».

  • Не так
    «v1.3.0 найновіший тег, отже, у ньому все, що є в main».

    Тег стоїть на одному коміті й не йде за main.

Ніколи не переставляй опублікований тег

Технічно тег можна видалити й створити деінде; у git навіть є git tag -f, щоб зробити це примусово. Але щойно тег опубліковано, його ніколи не переставляють.

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

Люди й інструменти покладаються на те, що v1.3.0 завжди означає той самий код. Підтримка вже сказала клієнтам «виправлено в 1.3.0». Партнер міг на нього зорієнтуватися. Нотатки до релізів, changelog і посилання на порівняння вказують на нього. Переставиш тег, і кожне з цих тверджень тихо змінить зміст. Хто вже отримав старий тег, лишиться зі старою позицією, тож двоє людей дивитимуться на «ту саму» версію й бачитимуть різний код.

Тож коли новій роботі потрібна версія, створи нову версію. Виправлення кнопки з'явилося після v1.3.0? Воно піде в наступний тег, v1.4.0, який міститиме все до власного коміту. Якщо реліз виявився зламаним, випусти виправлення новою PATCH-версією, як-от v1.4.1, а не переписуй стару.

Номери версій дешеві. Довіра до того, що означає номер версії, ні.

  • Так
    Поставити тег v1.4.0 на найновіший коміт main; v1.3.0 лишити на місці.

    Нова робота, нова версія.

  • Не так
    git tag -f v1.3.0 на сьогоднішній main, а тоді пуш ще раз.

    Кожне «виправлено в 1.3.0» стає неправдою.

Як читати номер версії: SemVer простими словами

Семантичне версіонування (SemVer) записує версію як MAJOR.MINOR.PATCH, і кожне число каже, яка зміна сталася:

  • PATCH зростає для виправлень: 1.4.0 → 1.4.1.
  • MINOR зростає для нових фіч, що нічого не ламають: 1.4.1 → 1.5.0.
  • MAJOR зростає для змін, що ламають щось, на що люди покладаються: 1.5.0 → 2.0.0.

Коли число зростає, усі праворуч від нього обнуляються. Після 1.3.0 виправлення дає 1.3.1, нова фіча 1.4.0, зміна, що ламає сумісність, 2.0.0.

У MAJOR.MINOR.PATCH після 1.3.0 виправлення дає 1.3.1, нова можливість, що нічого не ламає, — 1.4.0, а несумісна зміна — 2.0.0.

Формальні правила на semver.org написані для програм, від яких залежать інші програми. Сайт має сам вирішити, що для нього означає «ламає». Правило Котокорму: MAJOR лише тоді, коли перестають працювати старі посилання, збережені кошики чи інтеграції партнерів.

Ще дві домовленості. v у v1.4.0 це просто поширена звичка для назв тегів; сама версія 1.4.0. А суфікс позначає пре-реліз, тестову версію перед справжньою: 1.5.0-beta.1 іде перед 1.5.0.

  • Так
    Після v1.4.0 поле для нотатки до подарунка → v1.5.0.

    Нова фіча: MINOR зростає, PATCH обнуляється.

  • Не так
    Після v1.4.0 виправлення посилання у футері → v1.5.1.

    Виправлення підвищує лише PATCH: v1.4.1.

Порівняння версій: числа, а не абетка

Чи 1.4.10 старша за 1.4.9? За абеткою так, бо «1» іде перед «9». У SemVer ні.

Порівнюй версії частина за частиною, зліва направо, кожну як ціле число: спершу MAJOR, потім MINOR, потім PATCH. Десять більше за дев'ять, тож 1.4.10 новіша за 1.4.9. Будь-яка 1.x старша за 2.0.0, хай які великі в неї інші числа.

Пре-релізи стоять одразу перед версією, до якої ведуть: 1.5.0-beta.1 новіша за будь-яку 1.4.x, але старша за саму 1.5.0. Тож по порядку:

v1.4.1 → v1.4.9 → v1.4.10 → v1.5.0-beta.1 → v1.5.0 → v2.0.0

Не довіряй порядку в списку наосліп. Сторінка тегів GitHub не завжди сортує теги за версіями, а звичайне сортування за абеткою помиляється з 1.4.10. У терміналі git tag --sort=v:refname порівнює числа як числа, хоча щоб суфікси пре-релізів стали перед своїм релізом, потрібне додаткове налаштування. Коли є сумнів, порівняй частини власноруч.

  • Так
    1.4.9 < 1.4.10: порівняй 9 і 10 як числа.

    Кожна частина ціле число.

  • Не так
    «1.5.0-beta.1 новіша за 1.5.0, у неї більше символів».

    Пре-реліз іде перед своїм релізом.

Публікація релізу на GitHub

Реліз на GitHub (GitHub release) це сторінка, побудована на git-тезі: номер версії, нотатки до релізу для людей і файли для завантаження. Тег позначає точний коміт, а реліз пояснює, що змінилося від попереднього. Усі релізи лежать у розділі Releases репозиторію.

Станом на 2026 рік кроки такі: Draft a new release, ввести новий тег, як-от v1.4.0, обрати ціль main (GitHub створить тег під час публікації), вказати попередній тег і натиснути Generate release notes. GitHub перелічить кожен pull request, змерджений від того тегу, з назвою й автором, плюс посилання на порівняння Full Changelog. Добрі назви pull requests тут окупаються вдруге.

Під текстом файли (assets): GitHub завжди додає вихідний код у zip і tar.gz, а команди застосунків прикріплюють зібрану програму. Далі дві позначки: Set as a pre-release для тестових версій і Set as the latest release, що дає найновішому повному релізу бейдж Latest. У терміналі: gh release create v1.4.0 --target main --generate-notes.

Два найновіші теги в catchow/website, четвер увечері
  • v1.4.0

    Latest · на 9c4a2d7 · нотатки від Daniel і Sofia

  • v1.3.0

    на 8be2f70 · без сторінки релізу, лише тег

У Котокорму публікація релізу нічого не викладає: кожна зміна вже була на сайті з моменту мерджу. Реліз називає й описує те, що там уже є.

  • Так
    gh release create v1.4.0 --target main --generate-notes

    Тег, ціль і згенеровані нотатки за раз.

  • Не так
    «Реліз опубліковано, отже, банер нарешті на сайті».

    У Котокорму деплой стався під час мерджу.

Нотатки до релізу для людей і changelog

Changelog це запис того, що змінилося в кожній версії, написаний для людей, які нею користуються. Добрі нотатки до релізу це такий запис для однієї версії. Список, який генерує GitHub, це сировина, а не готовий текст: підтримка не зрозуміє «Add loyalty points behind a flag» і не має розповідати клієнтам про роботу, яка ще прихована.

Що згенерувала кнопка Generate release notes · є вище

Тож нагорі дай коротку частину «Для клієнтів і підтримки» простими словами:

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

Згенерований список лиши нижче, для інженерів.

Деякі команди ведуть файл CHANGELOG.md із розділами Added, Changed і Fixed; інші використовують сторінки релізів як changelog. Невеликий файл налаштувань .github/release.yml може змусити генератор групувати pull requests за мітками, щоб усе з міткою bug потрапляло в «Fixes».

Читай кожен рядок з одним питанням: чи може підтримка переслати це клієнту як є?

  • Так
    «Виправлено: кнопка оформлення більше не лишається сірою після змін у кошику».

    Прості слова, які підтримка може переслати.

  • Не так
    «* Add loyalty points behind a flag by @catchow-daniel in #155»

    Мова інженерів, ще й видає приховану роботу.

Змерджено, випущено, видно: три різні моменти

За питанням «це вже на сайті?» ховаються три окремі питання:

  • Змерджено: чи зміна в main (або develop)?
  • Випущено: чи вона в іменованій версії, тезі з нотатками до релізу?
  • Видно: чи можуть клієнти нею справді користуватися?
Посилка з кормом проходить крізь троє дверей поспіль: двері майстерні, двері зі стрічкою й відчинені двері крамниці з покупцями.
Перш ніж відповідати, чи це вже на сайті, спитай себе: змерджено, випущено чи видно покупцям?

Наскільки вони рознесені в часі, залежить від команди. У Котокорму змерджено означає «на сайті за кілька хвилин», а реліз з'являється пізніше як іменований знімок: #134 був на сайті з минулої середи, а частиною v1.4.0 став у четвер. Флаги навіть тут відділяють випущене від видимого: #155 є у v1.4.0, але жоден клієнт його не бачить, поки loyalty-points вимкнено.

У команді застосунку з git flow усі три далеко одне від одного. Фіча, змерджена в develop, чекає на релізну гілку, потім на реліз і перевірку в магазинах. Навіть після схвалення магазини можуть розгортати версію поступово на дедалі більшу частку користувачів, тож «випущено» може означати «поки лише в частини». І ніхто нічого не побачить, доки не оновиться.

Тож перш ніж відповісти «чи вже на сайті?», визнач, про який із трьох моментів питають, і відповідай саме на нього.

  • Так
    «#155 змерджено й випущено у v1.4.0, але приховано: флаг вимкнено».

    Усі три моменти названо.

  • Не так
    «Це у v1.4.0, отже, кожен клієнт може цим користуватися».

    Флаг чи поступове розгортання все ще можуть це ховати.

Порівняння тегів і версія хотфіксу

Коли в п'ятницю посилання меню лояльності стало видимим, виправлення пройшло GitHub flow: малий pull request у main, рев'ю, перевірка на preview, squash-мердж як e5f81a3 і деплой. У GitHub flow хотфікс не окремий тип гілки, а просто малий pull request поза чергою. Від v1.4.0 нічого іншого не зливали, тож це став PATCH-реліз: v1.4.1. Якби туди проскочила нова фіча, чесним номером був би v1.5.0.

Щоб побачити, що саме одна версія додала порівняно з іншою, відкрий порівняння (compare view): v1.4.0...v1.4.1 на GitHub або git log --oneline v1.4.0..v1.4.1 у терміналі. Воно показує коміти, досяжні з другого тегу, але не з першого. Тут це один коміт, e5f81a3 (#158).

Саме тому нотатки до релізу перелічують лише нове від попередньої версії. #134 немає в нотатках v1.4.1, але він є у v1.4.1: він уже був у v1.4.0, а v1.4.1 стоїть далі на тій самій лінії, тож містить усе, що й v1.4.0.

  • Так
    git log --oneline v1.4.0..v1.4.1 → один коміт, #158

    Саме те, що додала нова версія.

  • Не так
    «#134 немає в нотатках v1.4.1, отже, у v1.4.1 його немає».

    Нотатки перелічують нове, а не все, що всередині.

Чи є виправлення в цій версії? Як перевірити

На питання «чи є #134 у v1.4.1?» є надійна відповідь і три способи її отримати:

  1. Сторінка коміту. Відкрий коміт #134, d51c7e8. Станом на 2026 рік GitHub показує теги, що його містять: v1.4.0 і v1.4.1, але не v1.3.0. У терміналі той самий список друкує git tag --contains d51c7e8.
  2. Історія. Подивися історію main до коміту з тегом v1.4.1; якщо d51c7e8 на цій лінії, версія його містить.
  3. Сторінки релізів по порядку. Знайди першу версію, у нотатках якої згадано виправлення, і перевір, що жоден пізніший реліз його не відкочує. Перша поява плюс жодного відкату відтоді: виправлення там.
main із двома новими тегамиКожен тег позначає один коміт. v1.4.1 стоїть далі на тій самій лінії, тож містить усе з v1.4.0 плюс e5f81a3.
  1. 8be2f70Add cat treats bundlev1.3.0
  2. 6f2c8a1Raise free-delivery threshold to 1000 UAH (#133)
  3. a7d93e4Rewrite delivery page copy (#132)
  4. d51c7e8Fix checkout button after cart edits (#134)
  5. 3b8e1f6Add autumn sale banner (#136)
  6. 9c4a2d7Add loyalty points behind a flag (#155)v1.4.0
  7. 0d7b3c9Check flag before showing menu linkfix/loyalty-menu-link
  8. e5f81a3Hide loyalty menu link when flag is off (#158)mainv1.4.1

Граф комітів, від найстарішого: main із двома новими тегами 1. коміт 8be2f70 у main "Add cat treats bundle", тег v1.3.0 2. коміт 6f2c8a1 у main "Raise free-delivery threshold to 1000 UAH (#133)" 3. коміт a7d93e4 у main "Rewrite delivery page copy (#132)" 4. коміт d51c7e8 у main "Fix checkout button after cart edits (#134)" (виділено) 5. коміт 3b8e1f6 у main "Add autumn sale banner (#136)" 6. коміт 9c4a2d7 у main "Add loyalty points behind a flag (#155)", тег v1.4.0 7. гілка fix/loyalty-menu-link починається від коміту 9c4a2d7 8. коміт 0d7b3c9 у fix/loyalty-menu-link "Check flag before showing menu link" 9. коміт e5f81a3: fix/loyalty-menu-link змерджено в main через squash "Hide loyalty menu link when flag is off (#158)", тег v1.4.1

Дві перевірки здаються зручними, але на питання не відповідають. Самі нотатки v1.4.1 перелічують лише нове від v1.4.0. А бейдж Merged каже лише, що зміна в main, але не в якій версії.

Відповідь, яку підтримка може переслати: «виправлено в 1.4.0 і в кожній версії відтоді», якщо нічого не відкочено. Це «якщо» важливе: пізніша версія може забрати зміну назад.

  • Так
    git tag --contains d51c7e8 → v1.4.0, v1.4.1

    Пряма й надійна відповідь.

  • Не так
    Пошукати #134 у нотатках v1.4.1 і відповісти «ні».

    Нотатки перелічують лише нове в цій версії.

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

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

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