Олег Марков
Аннотированный тег в Git git tag -a
Введение
Аннотированные теги в Git используются для того, чтобы отмечать важные точки в истории репозитория — чаще всего релизы, версии и крупные изменения. В отличие от «легковесных» тегов, аннотированный тег хранит больше информации и ведёт себя фактически как полноценный объект в истории Git.
Смотрите, я покажу вам, чем аннотированный тег полезен:
- он хранит автора и дату;
- содержит сообщение (описание релиза, список изменений и т.п.);
- может быть подписан GPG для проверки подлинности;
- переносится на удалённые репозитории и ведёт себя предсказуемо.
Во многих командах практика такая: каждую версию приложения отмечают аннотированным тегом, а сборка и деплой завязаны именно на эти теги. Поэтому важно хорошо понимать, как работает git tag -a, какие есть варианты его использования и с какими подводными камнями вы можете столкнуться.
Давайте разберёмся подробно.
Что такое аннотированный тег в Git
Объект тега в Git
Когда вы создаёте аннотированный тег, Git создаёт отдельный объект в своём внутреннем хранилище (.git/objects). Этот объект:
- ссылается на определённый коммит (или другой объект, но для нас важны коммиты);
- содержит имя тега;
- хранит автора и дату создания;
- содержит текст сообщения (аннотацию);
- может содержать GPG-подпись (если тег подписан).
То есть аннотированный тег — это не просто имя для коммита. Это полноценный объект, у которого есть метаданные. Поэтому его часто используют как «официальную» точку релиза.
Отличие аннотированного тега от легковесного
Чтобы вам было проще, сравним два вида тегов:
Легковесный тег:
- создаётся командой
git tag v1.0.0; - это просто имя (указатель) на коммит;
- не хранит автора и дату отдельно от коммита;
- сообщение отсутствует;
- подписать такой тег нельзя, потому что он не объект.
- создаётся командой
Аннотированный тег:
- создаётся командой
git tag -a v1.0.0; - это отдельный объект, который ссылается на коммит;
- хранит автора тега и дату создания тега;
- содержит сообщение (например, краткий changelog);
- может быть подписан GPG.
- создаётся командой
Вы можете думать о легковесном теге как о «быстрой закладке», а о аннотированном — как о «официальной точке версии с документом».
Создание аннотированного тега
Базовый пример использования git tag -a
Давайте начнём с самого простого — создать аннотированный тег для текущего коммита:
git tag -a v1.0.0
# Откроется редактор (vim, nano или другой), где вы напишете сообщение тега
Git откроет редактор, заданный в настройках, и предложит вам ввести текст аннотации. Обычно туда пишут:
- краткое описание релиза;
- список ключевых изменений;
- иногда ссылку на задачу в трекере.
Если вы хотите указать сообщение сразу в командной строке, можно сделать так:
git tag -a v1.0.0 -m "Первый стабильный релиз"
# -a - создаём аннотированный тег
# -m - указываем сообщение тега прямо в командной строке
Теперь тег v1.0.0 будет указывать на текущий коммит, а в истории появится объект тега с аннотацией.
Создание тега для конкретного коммита
Очень часто нужно поставить тег не на последний коммит, а на конкретный, который уже есть в истории. Например, вы забыли проставить тег в момент релиза.
Вот так вы можете выбрать нужный коммит:
git log --oneline
# 9fceb02 Исправлен баг в обработке ошибок
# 105fd2a Добавлена авторизация
# a3c6e5b Начальная версия приложения
Теперь давайте создадим тег для коммита 105fd2a:
git tag -a v0.9.0 105fd2a -m "Релиз версии 0.9.0 - добавлена авторизация"
# v0.9.0 - имя тега
# 105fd2a - хеш коммита, на который указывает тег
# -m - сообщение тега
Обратите внимание: если вы не укажете хеш, тег будет создан на текущем HEAD (активном коммите).
Выбор имени тега
Обычно имена тегов используют в формате семантического версионирования:
v1.0.0v1.2.3release-2024-01-15backend-v2.1.0
Жёстких правил нет, но практично:
- соблюдать единый формат в команде;
- не использовать пробелы;
- избегать слишком сложных схем, которые сложно запомнить.
Если вы используете CI/CD, теги часто автоматически подхватываются сборочной системой, поэтому лучше не менять схему именования слишком часто.
Просмотр аннотированных тегов и их содержимого
Список всех тегов
Чтобы увидеть все теги в репозитории, выполните:
git tag
# Выводит список тегов по алфавиту:
# v0.1.0
# v0.9.0
# v1.0.0
Смотрите, здесь Git никак не показывает, какой тег аннотированный, а какой легковесный. Но стандартная рекомендация — для релизов использовать именно аннотированные теги.
Если тегов много, их можно отфильтровать по шаблону:
git tag -l "v1.*"
# Выведутся только теги, начинающиеся на v1.
Просмотр подробностей аннотированного тега
Чтобы увидеть, к какому коммиту привязан тег v1.0.0 и какое у него сообщение, выполните:
git show v1.0.0
# Примерный вывод:
# tag v1.0.0
# Tagger: Имя Автора <email@example.com>
# Date: Tue Dec 5 10:45:12 2023 +0300
#
# Первый стабильный релиз
#
# commit 9fceb02...
# Author: ...
# Date: ...
#
# Сообщение коммита...
Обратите внимание, как здесь отображается:
- информация о теге (кто создал, когда, какое сообщение тега);
- затем содержимое коммита, на который тег указывает.
Если вы хотите просмотреть только аннотацию тега, без диффа коммита, можно использовать:
git show --no-patch v1.0.0
# --no-patch - не показывать diff
Просмотр различий между тегами
Аннотированные теги очень удобно использовать для сравнения версий. Например, вы хотите увидеть изменения между v1.0.0 и v1.1.0:
git diff v1.0.0 v1.1.0
# Показывает различия между двумя релизами
Или посмотреть список коммитов между двумя версиями:
git log v1.0.0..v1.1.0 --oneline
# Коммиты, которые попали в версию 1.1.0 по сравнению с 1.0.0
Здесь теги выступают как удобные «якоря», между которыми вы можете анализировать историю.
Изменение, удаление и переопределение аннотированных тегов
Переопределение тега на другой коммит
Иногда тег создают по ошибке не на тот коммит. Покажу вам, как это исправить.
Допустим, тег v1.0.0 должен указывать на коммит 9fceb02, а вы случайно создали его на другом:
git tag -a v1.0.0 -f 9fceb02 -m "Поправленный релиз 1.0.0"
# -f или --force - принудительно перезаписать существующий тег
Комментарий важный: если тег уже был отправлен на удалённый репозиторий и другие разработчики его успели забрать, переопределение приведёт к рассинхронизации. Тогда вам придётся:
- принудительно обновить тег на удалённом репозитории;
- попросить коллег перезабрать теги (например, через
git fetch --tags --force).
Удаление локального аннотированного тега
Чтобы удалить тег в локальном репозитории:
git tag -d v1.0.0
# -d - удалить тег по имени
Важно: это удалит только локальный тег. Если он уже есть в удалённом репозитории, там он останется.
Удаление тега на удалённом репозитории
Если вы хотите удалить тег и в удалённом репозитории (например, на GitHub или GitLab), сначала удалите локальный, потом выполните:
git push origin :refs/tags/v1.0.0
# Пушим "пустую" ссылку вместо тега - так удаляется тег на удалённом репо
Или более короткий синтаксис, который многие используют:
git push origin :v1.0.0
# Работает как удаление ссылки v1.0.0 на удалённой стороне
Теперь тег будет удалён и локально (если вы сделали git tag -d) и на удалённом репозитории.
Переименование аннотированного тега
В Git нет прямой команды «переименовать тег», но можно сделать это в два шага:
git tag old-tag
# Предположим, у нас есть тег old-tag, который нужно переименовать в new-tag
# 1. Создаём новый тег, указывающий на тот же коммит
git tag -a new-tag old-tag -m "Переименованный тег"
# 2. Удаляем старый тег локально
git tag -d old-tag
# 3. Обновляем теги на удалённом репозитории
git push origin new-tag # отправляем новый тег
git push origin :old-tag # удаляем старый тег на удалённом репо
Обратите внимание, что вы фактически создаёте новый объект тега с другим именем, но на тот же коммит. Старый при этом удаляется.
Работа с аннотированными тегами и удалёнными репозиториями
Отправка аннотированных тегов на удалённый репозиторий
По умолчанию команда git push не всегда отправляет теги. Часто разработчики удивляются, почему тег есть у них локально, но не виден на GitHub.
Есть три распространённых варианта:
Отправить один конкретный тег:
git push origin v1.0.0 # Отправляем только тег v1.0.0Отправить все локальные теги:
git push origin --tags # Все теги, которых нет в origin, будут отправленыНастроить
push.followTags, чтобы при обычном пуше отправлялись только теги, указывающие на пушаемые коммиты:git config --global push.followTags true # Теперь git push будет автоматически отправлять теги, # которые указывают на коммиты, входящие в пуш
Этот последний вариант удобен: вы не отправляете сразу все теги за всю историю, а только те, которые связаны с текущими изменениями.
Получение тегов из удалённого репозитория
Если вы клонировали репозиторий давно, а теги были добавлены позже, вы можете их не видеть. Тогда достаточно выполнить:
git fetch --tags
# Получаем все теги с удалённого репозитория
Если вы хотите обновить теги с учетом возможных переопределений (это редкий, но возможный вариант), можно добавить --force, но делать это нужно аккуратно:
git fetch --tags --force
# Перезаписываем локальные теги новыми версиями с удалённого репо
Аннотированные теги и подписи (GPG)
Зачем подписывать теги
Подписанные аннотированные теги позволяют вам:
- доказать, что тег был создан конкретным человеком, у которого есть приватный ключ;
- защититься от подмены релизов;
- проверить подлинность версии в цепочке поставки.
В open-source проектах это очень распространённая практика: релиз версии подписан ключом мейнтейнера.
Создание подписанного аннотированного тега
Предположим, у вас уже настроен GPG-ключ и Git знает, каким ключом подписывать. Тогда вы можете создать подписанный тег так:
git tag -s v1.0.0 -m "Релиз 1.0.0 - подписанный"
# -s - создать подписанный тег (signed tag)
# -m - сообщение тега
Если нужно явно указать ключ, можно использовать:
git tag -u <key-id> -a v1.0.1 -m "Релиз 1.0.1 с конкретным ключом"
# -u <key-id> - использовать указанный GPG-ключ
Под капотом будет создан объект тега, содержащий GPG-подпись.
Проверка подписи тега
Чтобы проверить подпись тега, используйте:
git tag -v v1.0.0
# -v - verify, проверить подпись
Git покажет, корректна ли подпись, и какой ключ использовался. Если ключ не известен локальной системе, вы увидите предупреждение и, возможно, захотите импортировать публичный ключ автора.
Практическое использование аннотированных тегов
Аннотированные теги и версии релизов
В типичном рабочем процессе релизы делаются так:
- Разработчики мержат фичи в основную ветку (
mainилиmaster). Когда состояние ветки стабильно, создаётся аннотированный тег, например:
git tag -a v2.3.0 -m "Релиз 2.3.0 - новые отчеты и исправления ошибок"Тег отправляется на удалённый репозиторий:
git push origin v2.3.0CI/CD-система настроена так, чтобы запускать сборку и деплой при появлении нового тега
v*.
Таким образом, аннотированный тег становится «официальной меткой релиза» и точкой, к которой можно всегда вернуться.
Создание changelog на основе тегов
Давайте посмотрим, как можно быстро собрать изменения между двумя тегами, чтобы составить changelog.
Например, вам нужно подготовить список изменений для версии v1.1.0 относительно v1.0.0:
git log v1.0.0..v1.1.0 --oneline --no-merges
# --oneline - краткий формат
# --no-merges - исключить merge-коммиты, чтобы оставить только реальные изменения
Здесь вы увидите список коммитов, которые попали в новый релиз. По этому списку удобно формировать человеческий changelog.
Можно сделать вывод с форматированием:
git log v1.0.0..v1.1.0 --pretty=format:"* %s (%h)"
# * Описание коммита (9fceb02)
# * Ещё одно изменение (105fd2a)
Такой список можно почти сразу переносить в документ с изменениями.
История по тегам
Чтобы быстро просмотреть, какие коммиты соответствуют каждому тегу, вы можете использовать:
git log --decorate --oneline --graph --all
# --decorate - показывает теги и ветки рядом с коммитами
# --graph - визуальное древо
# --all - все ветки
В выводе вы увидите что-то вроде:
* 9fceb02 (HEAD -> main, tag: v1.1.0) Исправлен баг...
* 105fd2a (tag: v1.0.0) Добавлена авторизация
* a3c6e5b (tag: v0.1.0) Начальная версия
Так вы можете легко отследить, какие коммиты были релизными.
Типичные ошибки и подводные камни
Создание легковесного тега вместо аннотированного
Иногда по привычке разработчик делает:
git tag v1.0.0
# Создаётся легковесный тег, а не аннотированный
А команда ждёт, что у тега будет сообщение, автор и дата. Чтобы исправить:
Удаляем легковесный тег:
git tag -d v1.0.0Создаём аннотированный с тем же именем:
git tag -a v1.0.0 -m "Релиз 1.0.0"Отправляем на удалённый репозиторий (если нужно):
git push origin v1.0.0
Локальный тег не виден на удалённом репозитории
Частая ситуация: вы создали тег, но забыли его отправить.
git tag -a v1.2.0 -m "Релиз 1.2.0"
git push origin main
# Тег не появился на GitHub, хотя коммиты ушли
Причина в том, что git push origin main не отправляет теги. Нужно отдельно:
git push origin v1.2.0
# или
git push origin --tags
Разные теги у разных разработчиков
Бывает, что один разработчик переопределяет тег (с -f), а другие уже успели его забрать. В результате:
- локальные репозитории у разных людей указывают одним и тем же тегом на разные коммиты;
- могут запускаться разные сборки по одному и тому же тегу.
Чтобы выровнять состояние, можно:
На стороне «источника истины» (обычно CI или мейнтейнер) обновить тег и запушить его с
--force, если нужно:git tag -a v1.0.0 -f <new-commit> -m "Обновлённый релиз" git push origin -f v1.0.0У остальных разработчиков:
git fetch --tags --force # Обновить локальные теги в соответствии с удалённым репозиторием
Однако лучше менять уже опубликованные теги как можно реже. Во многих командах действуют негласные правила: если тег ушёл на origin, его не трогают, а если версия неудачна — создают новый тег (например, v1.0.1).
Рекомендованный рабочий процесс с аннотированными тегами
Соберу воедино практический сценарий, как вы можете использовать аннотированные теги в реальном проекте.
1. Подготовка к релизу
- Все нужные фичи вмержены в основную ветку (
main). - Тесты проходят, приложение работает корректно.
Проверяем состояние:
git checkout main
git pull origin main
git status
# Убедитесь, что всё чисто и актуально
2. Выбор версии
Вы решаете, какой номер версии будет у релиза: v1.3.0, например, по правилам семантического версионирования:
- MAJOR — ломающее изменение API;
- MINOR — новая функциональность, совместимая назад;
- PATCH — багфиксы.
3. Создание аннотированного тега
git tag -a v1.3.0 -m "Релиз 1.3.0 - новая статистика и оптимизация запросов"
Если вы используете подписи:
git tag -s v1.3.0 -m "Релиз 1.3.0 - подписанный релиз"
4. Отправка тега на удалённый репозиторий
git push origin v1.3.0
# Или если вы впервые отправляете много тегов:
git push origin --tags
5. Интеграция с CI/CD
На стороне CI можно настроить, например:
- запуск пайплайна только при появлении нового тега вида
v*; - сборку артефактов с именем, содержащим тег;
- деплой на production только по тегам.
В таком случае аннотированный тег становится триггером для публикации версии.
Аннотированные теги — важный инструмент, который связывает техническую историю коммитов с человечески понятными версиями и релизами. Они обеспечивают:
- удобную навигацию по истории,
- возможность документировать релизы,
- поддержку подписи и проверки подлинности,
- предсказуемую интеграцию с инструментами доставки.
Используя git tag -a как стандарт для релизов, вы упрощаете жизнь и себе, и всей команде: всегда понятно, что именно вошло в конкретную версию, на какой коммит она указывает и как к ней вернуться.
Частозадаваемые технические вопросы и ответы
1. Как изменить сообщение уже созданного аннотированного тега
Напрямую переписать сообщение тега нельзя, но можно пересоздать тег:
# 1. Узнаём, на какой коммит указывает тег
git rev-parse v1.0.0
# 2. Удаляем старый тег локально
git tag -d v1.0.0
# 3. Создаём его заново с тем же коммитом, но новым сообщением
git tag -a v1.0.0 <commit-hash> -m "Новое сообщение тега"
# 4. Обновляем тег на удалённом репо (если он уже был там)
git push origin -f v1.0.0
2. Как понять, аннотированный тег или легковесный
Проще всего — через git cat-file:
git cat-file -t v1.0.0
# tag - если аннотированный
# commit - если легковесный (это просто ссылка на коммит)
Или посмотрите git show v1.0.0: у аннотированного тега будет раздел tag с автором и сообщением.
3. Как создать аннотированный тег на основе уже существующего легковесного
# 1. Узнаём, на какой коммит указывает легковесный тег
git rev-parse v1.0.0
# 2. Удаляем легковесный тег
git tag -d v1.0.0
# 3. Создаём аннотированный тег на тот же коммит
git tag -a v1.0.0 <commit-hash> -m "Аннотированная версия тега v1.0.0"
4. Как вывести только аннотацию тега без деталей коммита
Используйте:
git for-each-ref refs/tags/v1.0.0 --format="%(contents)"
# Показывает только сообщение тега
Либо:
git show --no-patch --pretty="format:%B" v1.0.0
# %B - тело сообщения тега
5. Как запретить принудительное перезаписывание тегов в репозитории
Многие серверы Git (GitLab, GitHub Enterprise, Gitea) позволяют настроить защиту тегов. Общая идея:
- в настройках репозитория включить «Protected tags»;
- указать шаблон (например,
v*); - запретить force-push и удаление таких тегов для большинства пользователей.
Так вы избежите случайного переопределения релизных аннотированных тегов. Настройка делается на стороне сервера Git через веб-интерфейс или конфигурацию, а локальные команды git tag -f перестанут менять такие теги на удалённом репозитории.
Постройте личный план изучения Git до уровня Middle — бесплатно!
Git — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Git
Лучшие курсы по теме

Основы Git
Антон Ларичев
TypeScript с нуля
Антон Ларичев