Теги и релизы
Теги именуют фиксированную точку истории, обычно релиз. Лёгкий тег — просто указатель; аннотированный тег (git tag -a) — настоящий объект с автором, датой и сообщением — используй его для релизов. Теги НЕ пушатся по умолчанию; отправляй их через git push --tags.
Через шесть недель после релиза клиент сообщает о баге «в версии 1.4.0». Какой коммит был 1.4.0? Ветки двигаются, и main теперь на сотни коммитов впереди, поэтому тебе нужна постоянная именованная закладка на точном коммите, который ты выпустил. Эта закладка — тег — и правильно выбрать его тип и push означает разницу между чистым процессом релиза и запутанным.
К концу этого урока ты сможешь пометить релиз правильно, понять, почему аннотированные теги лучше лёгких для релизов, осознанно пушить теги и читать номер версии как инженер.
После этого урока ты сможешь создавать лёгкие и аннотированные теги, выбирать правильный для релиза, пушить теги на удалённый сервер (зная, что автоматически они не пушатся), осматривать тег и объяснять, что сообщает номер семантической версии.
Тег — это имя, постоянно прикреплённое к одному коммиту, обычно к точке релиза. В отличие от ветки, тег не двигается, когда ты добавляешь новые коммиты; он остаётся приколотым к коммиту, который пометил. Смотреть и осматривать теги:
git tag # перечислить все теги
git tag -l "v1.4.*" # перечислить теги по шаблону
git show v1.4.0 # показать, на что указывает тегЭто даёт стабильный словарь: «баг в v1.4.0» навсегда отображается на один точный коммит, как бы далеко ни ушёл main.
Есть два вида тегов, и разница важна для релизов. Лёгкий тег — это просто имя, указывающее на коммит, и ничего больше. Аннотированный тег — это полноценный объект в базе git, хранящий имя автора тега, email, дату и сообщение (и может быть подписан GPG):
git tag v1.4.0-tmp # лёгкий: голый указатель, без метаданных
git tag -a v1.4.0 -m "Релиз 1.4.0" # аннотированный: настоящий объект с автором + датой + сообщениемИспользуй лёгкие теги для приватных одноразовых закладок. Используй аннотированные теги для всего, что выпускаешь, потому что метаданные — кто нарезал релиз, когда, и сообщение — это ровно то, на что позже опирается аудит или git describe.
Теги НЕ пушатся по умолчанию — их надо отправлять явно. Обычный git push двигает коммиты веток, но оставляет твои новые теги лежать локально. Два способа их опубликовать:
git push origin v1.4.0 # запушить один конкретный тег
git push origin --tags # запушить ВСЕ локальные теги, которых ещё нет на сервереНа этом спотыкаются почти все хотя бы раз: ты помечаешь релиз, пушишь, анонсируешь — а тега нигде нет на сервере, потому что ты его так и не отправил. Когда автоматизация (CI-пайплайны релиза) следит за тегами, забытый --tags молча пропускает твою релизную сборку.
Номера версий не произвольны: семантическое версионирование кодирует совместимость. Строка semver — это MAJOR.MINOR.PATCH, и у каждой части есть контракт:
v 1 . 4 . 2
│ │ └─ PATCH: обратносовместимые исправления багов
│ └───────── MINOR: новые функции, всё ещё обратносовместимо
└───────────────── MAJOR: ломающие изменения (потребители должны адаптироваться)Поднятие MAJOR сигналит «я сломал API»; MINOR говорит «новые функции, твой код всё ещё работает»; PATCH говорит «только исправления багов». Это язык, на котором твои теги говорят со всеми ниже по течению, поэтому имя тега несёт реальный смысл, а не просто ярлык.
▸Граничные случаи
Checkout тега даёт отсоединённый HEAD. Поскольку тег — не ветка, git checkout v1.4.0 высаживает тебя на коммит без прикреплённой ветки — HEAD указывает прямо на коммит. Это нормально для осмотра или сборки старого релиза, но любые коммиты, сделанные там, не принадлежат никакой ветке и могут быть потеряны. Если нужно что-то починить, начиная с тега, ответвись от него явно: git switch -c hotfix/1.4.1 v1.4.0.
Нарезка и публикация релиза.
Тесты зелёные на main, и ты выпускаешь 2.0.0 — релиз, который выкидывает устаревший эндпоинт, так что это ломающее (MAJOR) изменение.
git switch main
git pull
git tag -a v2.0.0 -m "Релиз 2.0.0: удаление устаревшего /v1 API"
git show v2.0.0 # подтвердить автора, дату, сообщение и целевой коммит
git push origin v2.0.0 # опубликовать тег, чтобы CI и потребители его увиделиАннотированный тег записывает, кто нарезал 2.0.0 и когда, номер 2.0.0 говорит каждому потребителю ожидать ломающих изменений, а явный push делает тег видимым на сервере, чтобы релизный пайплайн сработал. Коллега, чинящий его позже, выполняет git switch -c hotfix/2.0.1 v2.0.0, а не коммитит на отсоединённом HEAD.
▸Частая ошибка
Две повторяющиеся ошибки релизного дня: использование лёгкого тега для настоящего релиза (так что нет записи, кто и когда выпустил), и забывание, что теги не пушатся по умолчанию (так что тег есть на ноутбуке, но никогда не доходит до сервера или CI). Сделай связку аннотированный-тег-плюс-явный-push своим фиксированным ритуалом. Поднятие не той части semver — выпуск ломающего изменения как PATCH — это третья ошибка: она тихо ломает всех, кто доверял номеру в значении «безопасное обновление».
Тег — это постоянное имя на одном коммите, используемое для пометки релизов. Лёгкий тег — голый указатель; аннотированный тег (git tag -a -m) — настоящий объект, хранящий автора, дату и сообщение — используй аннотированный для всего, что выпускаешь, и git tag -s, чтобы подписать его. Теги не пушатся по умолчанию: публикуй их через git push origin <tag> или --tags, иначе твой релиз останется невидимым для сервера и CI. Версионируй по semver (MAJOR.MINOR.PATCH), чтобы сам номер сообщал совместимость, и помни, что checkout тега даёт отсоединённый HEAD — ответвись от него, чтобы вносить правки. Дальше ты будешь запускать два рабочих дерева из одного репозитория с помощью git worktree.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.