Tags no Git: como usar o git tag e versionar releases
O git tag coloca um marcador permanente em um commit específico, normalmente para identificar um release, como v1.2.0. Uma tag anotada é criada com git tag -a v1.2.0 -m "mensagem", e as tags são enviadas ao servidor separadamente: git push origin v1.2.0 ou git push --tags.
git tag é um marcador com nome em um commit específico. Diferente de uma branch, a tag não se move com novos commits: daqui a um ano, v1.2.0 vai apontar para o mesmo snapshot do código. Por isso as tags são usadas para releases, e sistemas de CI/CD muitas vezes disparam o build e a publicação justamente quando surge uma tag.
Tags leves e anotadas
O Git tem dois tipos de tag, e a diferença importa.
| Leve (lightweight) | Anotada (annotated) | |
|---|---|---|
| Criação | git tag v1.0.0 | git tag -a v1.0.0 -m "..." |
| O que é | apenas um ponteiro para um commit | um objeto próprio no banco do Git |
| Autor, data, mensagem | não | sim |
| Assinatura GPG/SSH | não | possível (-s) |
Considerada pelo git describe por padrão | não | sim |
| Quando usar | marcadores pessoais temporários | releases e tudo o que é publicado |
Para releases, use tags anotadas: elas registram quem lançou a versão e quando, e podem ser assinadas.
Como criar uma tag
No commit atual
git tag -a v1.2.0 -m "Release 1.2.0: exportação de relatórios, timeout da API corrigido"
Sem -m, o Git abre o editor para a mensagem — prático se você quiser escrever a lista de alterações.
Em um commit antigo
Esqueceu de criar a tag no release? Sem problema, a tag pode ser colocada em qualquer commit:
git log --oneline -5
# 7be41d0 feat: exportação de relatórios
# 1c02e8f fix: timeout das requisições
git tag -a v1.1.0 1c02e8f -m "Release 1.1.0"
Tag assinada
git tag -s v1.2.0 -m "Release 1.2.0"
git tag -v v1.2.0 # verificar a assinatura
É preciso ter uma chave GPG configurada (ou SSH com gpg.format=ssh). GitHub e GitLab mostram essas tags com a marca de verificação.
Como listar as tags
git tag # todas as tags em ordem alfabética
git tag -l "v1.*" # por padrão
git tag -n # com a primeira linha da mensagem
git tag --sort=-v:refname # por versão, as mais novas primeiro
git show v1.2.0 # dados da tag e do commit
git describe --tags # tag mais próxima: v1.2.0-3-g7be41d0
Atenção à ordenação: por padrão v1.10.0 aparece antes de v1.9.0, porque as strings são comparadas caractere a caractere. A opção --sort=v:refname ordena como versões.
A saída do git describe se lê assim: três commits depois da tag v1.2.0, commit atual 7be41d0 (a letra g significa git). É uma fonte prática de número de versão para builds entre releases.
Como enviar uma tag para o servidor
O git push comum não envia tags — essa é a causa frequente da pergunta "por que a tag não aparece no GitHub".
git push origin v1.2.0 # uma tag
git push --tags # todas as tags locais
git push --follow-tags # commits + tags anotadas que apontam para eles
O --follow-tags é mais seguro que o --tags: ele não envia tags leves acidentais que você criou para si. Dá para ativá-lo por padrão: git config --global push.followTags true.
Para baixar as tags do servidor: git fetch --tags. O git fetch comum também traz as tags que apontam para os commits baixados.
Como excluir ou mover uma tag
Exclusão
git tag -d v1.2.0 # localmente
git push origin --delete v1.2.0 # no servidor
# equivalente antigo: git push origin :refs/tags/v1.2.0
Mover a tag para outro commit
git tag -a -f v1.2.0 <novo-hash> -m "Release 1.2.0"
git push origin -f v1.2.0
Tecnicamente é possível, mas em projetos públicos não vale mover uma tag já publicada. Quem já baixou a tag não vai recebê-la atualizada automaticamente: o Git não sobrescreve tags locais existentes no fetch sem --force. No fim, "v1.2.0" será um código diferente para pessoas diferentes. O certo é lançar a v1.2.1.
Como saber quais commits entraram em um release
As tags são práticas como limites de intervalos em todos os comandos de leitura do histórico:
git log v1.1.0..v1.2.0 --oneline # commits entre releases
git diff v1.1.0 v1.2.0 --stat # quais arquivos mudaram
git tag --contains 1c02e8f # em quais releases está este commit
git merge-base --is-ancestor 1c02e8f v1.2.0 && echo "está no release"
Os dois últimos comandos respondem a uma pergunta frequente do suporte: "a correção já chegou aos usuários ou não?" Se a correção entrou só na main e o release foi gerado antes, o git tag --contains mostra uma lista vazia, e fica claro que é preciso uma nova tag.
Outro truque é baixar o código-fonte de uma versão específica sem histórico: git archive --format=zip -o app-1.2.0.zip v1.2.0. Assim se geram pacotes para entrega ao cliente ou para build em ambiente isolado.
Como mudar para uma versão pela tag
git switch --detach v1.1.0
# HEAD is now at 1c02e8f fix: timeout das requisições
Você ficará em detached HEAD: dá para ver e compilar, mas os novos commits não pertencerão a nenhuma branch. Se precisar corrigir uma versão antiga, crie uma branch a partir da tag:
git switch -c hotfix/1.1.x v1.1.0
# alterações, commit
git tag -a v1.1.1 -m "Correção de segurança"
git push origin hotfix/1.1.x --follow-tags
Versionamento de releases: SemVer
O esquema de nomes de tags mais comum é o Semantic Versioning: MAJOR.MINOR.PATCH.
- MAJOR sobe com mudanças incompatíveis na API:
1.4.2 → 2.0.0. - MINOR — nova funcionalidade mantendo a compatibilidade:
1.4.2 → 1.5.0. - PATCH — correções de bugs:
1.4.2 → 1.4.3. - Pré-releases levam um sufixo:
2.0.0-beta.1,2.0.0-rc.1.
O prefixo v (v1.4.2) é uma convenção comum para nomes de tags; o SemVer em si não exige. O importante é escolher um formato e mantê-lo, senão os scripts que procuram o último release quebram.
Tags no processo de release
Um processo típico em equipe é assim:
- As alterações são integradas na main, testes verdes.
- Atualiza-se o CHANGELOG e o número da versão nos arquivos do projeto, se ele estiver lá.
- Cria-se uma tag anotada:
git tag -a v1.5.0 -m "Release 1.5.0". git push origin main --follow-tags.- O CI detecta a nova tag, gera os artefatos e publica o release; no GitHub e no GitLab, a partir da tag é criada uma página de Release com descrição e arquivos.
Assim, qualquer versão em produção pode ser associada sem ambiguidade ao código: a tag no nome do build — e com git show dá para ver o que entrou nele. Já git log v1.4.0..v1.5.0 --oneline dá a lista de alterações entre releases para as release notes.
Release notes a partir das tags
Se as mensagens de commit forem claras, as tags transformam o histórico em uma lista de alterações pronta. Dá para encontrar o último release e o anterior sem procurar à mão:
LAST=$(git describe --tags --abbrev=0)
PREV=$(git describe --tags --abbrev=0 "$LAST^")
git log "$PREV..$LAST" --no-merges --pretty=format:"- %s (%h)"
# - feat: exportação de relatórios (7be41d0)
# - fix: timeout das requisições (1c02e8f)
Se o time escreve commits seguindo os Conventional Commits (feat:, fix:, feat!:), o número da próxima versão pode ser calculado automaticamente: fix sobe o PATCH, feat o MINOR, ponto de exclamação ou BREAKING CHANGE o MAJOR. É nisso que se baseiam as ferramentas populares de release automático: elas mesmas criam a tag e escrevem o CHANGELOG.
Tags e CI/CD
Os pipelines costumam distinguir dois eventos: push em uma branch e criação de uma tag. Na branch rodam os testes e o build do ambiente de teste; na tag, a publicação. No GitHub Actions fica assim:
on:
push:
tags:
- "v*.*.*"
No GitLab CI, o mesmo papel é feito pela variável CI_COMMIT_TAG e pela regra rules: - if: $CI_COMMIT_TAG. O nome da tag dentro do pipeline vira o número de versão da imagem Docker ou do pacote, por isso o formato das tags deve ser rígido e previsível.
Daí mais um motivo para não mover tags: se o pipeline já publicou o pacote 1.2.0 no registry, uma nova publicação com o mesmo número normalmente é proibida, e a imagem com a tag 1.2.0 continuará antiga para os usuários.
Nomes de tags: o que é permitido
O nome de uma tag segue as mesmas regras do nome de uma branch: sem espaços, sem os caracteres ~ ^ : ? * [ e sem dois pontos seguidos. A barra é permitida, por isso em monorepos aparecem tags como api/v1.2.0 e web/v3.0.1 — um conjunto de versões por pacote. Para validar o nome antes, use git check-ref-format "refs/tags/v1.2.0": em caso de erro, ele retorna um código diferente de zero.
Proteção de tags
Em repositórios de equipe, vale proteger as tags de release: GitHub e GitLab permitem bloquear a criação, a sobrescrita e a exclusão de tags por padrão (por exemplo, v*) para todos, exceto os mantenedores. Assim, um git push --tags acidental de outra máquina não publica um release não planejado, e um git push origin --delete por engano não apaga o histórico de versões.
Erros comuns
- A tag não está no servidor. Esqueceu o
git push origin <tag>ou o--follow-tags. - Tag com o mesmo nome de uma branch. O Git avisa
refname is ambiguous; use o prefixovpara as tags e nomes diferentes. - Tag em um estado não commitado. A tag é colocada em um commit, não nos arquivos: alterações não commitadas não entram no release.
- Tag leve para release. Sem autor, data e mensagem, e o
git describenão a enxerga por padrão.
Resumo
O git tag fixa um ponto imutável no histórico: para releases, crie tags anotadas com git tag -a, nomeie-as seguindo o SemVer e envie-as ao servidor explicitamente — git push origin <tag> ou --follow-tags. Não mova tags publicadas; lance uma nova versão. Tags, junto com rebase, stash e cherry-pick, são praticadas no curso de Git.
Perguntas frequentes
Qual a diferença entre tag e branch?
A branch avança automaticamente a cada novo commit, enquanto a tag aponta para sempre para o mesmo commit.
Dá para colocar duas tags no mesmo commit?
Sim, não há limite de tags por commit, por exemplo v2.0.0 e latest-stable.
As tags são apagadas quando a branch é apagada?
Não, as tags são independentes das branches, e o commit com tag continua acessível mesmo depois de a branch ser apagada.
Como saber em qual release um commit entrou?
Rode git tag --contains <hash> — o comando mostra todas as tags cujo histórico inclui esse commit.
Como obter a última tag em um script?
Use git describe --tags --abbrev=0 — ele mostra o nome da tag mais próxima sem o sufixo com o hash.
Este tema é abordado em profundidade, com prática, no curso “Git do zero ao desenvolvedor confiante”.
Ir para o curso