Learning Platform
Глоссарий Troubleshooting
Урок 12.02 · 20 мин
Начальный
tagsannotatedlightweightreleasesemverversioning

Теги: lightweight vs annotated

Тег в Git — это именованный указатель на коммит. В отличие от ветки, он не двигается — если поставил v1.0.0 на коммит abc1234, он там и останется навсегда (пока не удалишь). Теги используют для версионирования: пометить «вот этот коммит — релиз 1.0.0».

В Git есть два типа тегов: lightweight и annotated. Поверхностно они выглядят одинаково (git tag v1.0.0 создаёт один, git tag -a v1.0.0 -m "..." — другой). Под капотом разница огромная — и для production releases правильный выбор всегда annotated.


Lightweight тег — просто указатель

$ git tag v1.0.0
$ ls .git/refs/tags/
v1.0.0

$ cat .git/refs/tags/v1.0.0
abc1234567890...

Это файл с хэшем коммита. Никаких метаданных — кто поставил, когда, зачем. Просто alias для коммита. Точно так же устроены ветки (refs/heads/), только без movement.

Преимущества:

  • Простота: ничего лишнего.
  • Можно ставить локально для своих заметок без церемоний.

Недостатки:

  • Нет author/date — кто и когда поставил тег? Неизвестно.
  • Нет message — что значил релиз? Неизвестно.
  • Не подписывается — нельзя верифицировать происхождение через GPG.

Lightweight tag — это «закладка в книге». Нормально для приватных заметок, плохо для публичных релизов.


Annotated тег — полноценный Git object

$ git tag -a v1.0.0 -m "Release 1.0.0 — initial production"

$ ls .git/refs/tags/
v1.0.0

$ cat .git/refs/tags/v1.0.0
def4567890abc...                   # хэш не коммита, а tag-object!

Annotated tag создаёт отдельный object в .git/objects, который хранит:

  • Хэш коммита, на который указывает.
  • Tag name.
  • Tagger (имя + email из git config).
  • Tag date.
  • Tag message.
  • Опциональная GPG signature.
$ git cat-file -p v1.0.0
object abc1234567890...
type commit
tag v1.0.0
tagger Иван Иванов <[email protected]m> 1715594400 +0300

Release 1.0.0 initial production

Это полноценная метаинформация о релизе: ясно кто релизнул, когда, с каким комментарием. Можно подписать GPG: git tag -s v1.0.0 -m "..." (требует настроенного GPG ключа).

Разница в Git objects: lightweight vs annotated
Просто файл с хэшем коммита, как ветка
refs/tags/v1.0.0
commit abc1234Указатель прямо на коммит
Полноценный object с метаданными
refs/tags/v1.0.0
tag def4567Tag object: tagger + date + message + signature
commit abc1234Tag object содержит указатель на commit

Когда что использовать

Annotatedвсегда для:

  • Релизов прода: v1.0.0, v2.3.1.
  • Внешних distributions: библиотеки, пакеты, образы Docker.
  • Любых тегов, которые видят другие.
  • GPG signed releases (security-critical).

Lightweightтолько для:

  • Локальных закладок: «запомнить этот коммит для дебага».
  • Временных меток в скриптах.

Production-правило DE-команд: в репо для Airflow/dbt/Spark — только annotated tags. Это видно в git tag --list через n1 flag (показать первую строку message):

$ git tag --list -n1
v1.0.0          Release 1.0.0 initial production
v1.1.0          Release 1.1.0 add user pipeline
v1.1.1          Hotfix 1.1.1 fix null handling

Lightweight теги в этом списке будут пустыми (нет message).


Создание тегов

# Annotated на HEAD
$ git tag -a v1.0.0 -m "Release 1.0.0"

# Annotated на конкретный коммит
$ git tag -a v0.9.0-beta abc1234 -m "Beta release"

# Lightweight (не рекомендую)
$ git tag v1.0.0-local

# Signed (GPG)
$ git tag -s v1.0.0 -m "Signed release 1.0.0"

После git tag тег существует только локально. Чтобы запушить:

# Один тег
$ git push origin v1.0.0

# Все теги
$ git push origin --tags

# С коммитами и тегами одновременно
$ git push --follow-tags
NOTE

По умолчанию git push НЕ пушит теги. Это сделано намеренно — теги не должны накапливаться от каждого push. Явно — --follow-tags (только annotated) или --tags (все).


Просмотр тегов

$ git tag                          # список всех тегов
$ git tag --list "v1.*"            # фильтр по паттерну
$ git tag --list -n3 "v1.*"        # с первыми 3 строками message
$ git show v1.0.0                  # детали тега + diff коммита
$ git log v1.0.0..HEAD             # коммиты между тегом и HEAD
$ git log --tags --simplify-by-decoration --oneline    # история тегов

Особенно полезен git log v1.0.0..v1.1.0 --oneline — что изменилось между релизами. Это автоматически генерируемый changelog.


Удаление тегов

# Локально
$ git tag -d v1.0.0

# Из remote (надо явно)
$ git push origin --delete v1.0.0

# Или старая форма (deprecated)
$ git push origin :refs/tags/v1.0.0
WARNING

Удалять опубликованные теги — плохая практика. Если кто-то закачал v1.0.0, а ты его удалил и заново поставил на другой коммит, у людей будут разные версии под одним именем. SemVer диктует: «опубликовал — не трогай». Если ошибся — выпусти v1.0.1 с корректным контентом.


Semantic Versioning basics

В DE-мире обычно используют SemVer: MAJOR.MINOR.PATCH.

ЧастьКогда увеличиватьПример
MAJORBreaking changes — обратной совместимости нет1.x -> 2.0
MINORНовые features, обратно совместимо1.0 -> 1.1
PATCHBug fixes, обратно совместимо1.1.0 -> 1.1.1

Pre-release suffixes:

  • v1.0.0-alpha.1, v1.0.0-beta.2, v1.0.0-rc.1

DE-сценарии:

ИзменениеBump
Добавили новый DAGminor (1.1.0 -> 1.2.0)
Исправили баг в существующем DAGpatch (1.2.0 -> 1.2.1)
Поменяли schema БД, требует миграцииmajor (1.2.1 -> 2.0.0)
Удалили deprecated tablemajor
Refactor без изменения поведенияminor или ничего

Для Airflow / dbt repos не всегда применимо строго (это не «библиотека для других»), но дисциплина SemVer помогает с changelog и rollback решениями.


Tag + GitHub Releases

GitHub автоматически распознаёт annotated tags как Releases. Workflow:

$ git tag -a v1.0.0 -m "Release 1.0.0"
$ git push origin v1.0.0

В GitHub UI появится Tags секция. Можно вручную создать Release из тега, прикрепить changelog, бинари, screenshots.

Через CLI:

$ gh release create v1.0.0 --notes "Initial production release"
$ gh release create v1.0.0 --notes-file CHANGELOG.md
$ gh release create v1.0.0 --generate-notes    # авто из PR

--generate-notes — GitHub читает merged PRs между предыдущим тегом и этим, генерирует changelog по их title-ам. Магически работает в team с дисциплиной PR-title.


DE-сценарий: версионирование dbt репо

Команда dbt держит models в Git. Каждый production-release — это тег.

# В feature ветке
$ git tag -a v2.3.1 -m "Hotfix: fix customer LTV calculation"
$ git push origin v2.3.1

# CI/CD pipeline на push tag
# 1. Парсит SemVer
# 2. Билдит dbt artifacts
# 3. Деплоит на prod warehouse
# 4. Создаёт GitHub Release с changelog

Tag триггерит deployment — это стандартный паттерн «tag-based releases». Альтернатива — branch-based (deploy on merge to release/2.3.x). Tag-based проще для аудита: ясно «v2.3.1 — это вот тот коммит, тот деплой».


Попробуй сам

$ mkdir tag-demo && cd tag-demo
$ git init
$ echo "v1" > app.py && git add . && git commit -m "Initial"
$ echo "v2" > app.py && git commit -am "Add feature"

# Lightweight
$ git tag v1.0-local

# Annotated
$ git tag -a v1.0.0 -m "First release"

# Сравни
$ git show v1.0-local      # просто коммит
$ git show v1.0.0          # есть tagger, date, message + diff

# В .git/objects
$ cat .git/refs/tags/v1.0-local      # хэш коммита
$ cat .git/refs/tags/v1.0.0          # хэш tag-object

# git tag -ln1
$ git tag -n1
v1.0-local
v1.0.0          First release          # видно только у annotated

# Diff между тегами
$ git diff v1.0-local..HEAD

Killer takeaway

Annotated tags (git tag -a v1.0.0 -m "...") — это full Git object с author/date/message/signature. Lightweight — просто файл с хэшем. Для любого релиза, который видит кто-то кроме тебя — только annotated. Push тегов не автоматический — git push --follow-tags или --tags. SemVer MAJOR.MINOR.PATCH — стандарт для версионирования. Tag-based deployment в CI — частый pattern DE-команд.

CI/CD для данных: деплой по тегу
Проверка знанийKnowledge check
ОтветAnswer

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Главная разница lightweight и annotated tag — что хранится в .git/objects?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4