gestor-tasques està a punt de tenir la seva primera versió estable. L'equip vol poder dir "això és la 1.0.0" i que d'aquí a dos anys, quan arribi una incidència d'un client que continua amb aquella versió, qualsevol pugui situar-se exactament en aquell codi sense dependre de recordar un hash de quaranta caràcters ni de remenar el git log per dates.

Per a això hi ha les etiquetes (tags): noms permanents que apunten a un commit concret. A diferència d'una branca, que avança cada vegada que confirmes, una etiqueta es queda quieta. v1.0.0 significa avui, demà i d'aquí a una dècada exactament el mateix commit.

I hi ha una segona cosa per aprendre aquí: a la lliçó 01-04 vam veure que la base de dades de Git desa quatre tipus d'objecte —blob, tree, commit i tag— i del quart amb prou feines vam dir res. Aquesta lliçó ho completa. Perquè resulta que hi ha dues classes d'etiqueta molt diferents, i la diferència entre elles és exactament si existeix o no aquest objecte tag.

Contingut

  1. Què és una etiqueta i en què es diferencia d'una branca
  2. Etiquetes lleugeres davant d'anotades
  3. Crear etiquetes
  4. Llistar, cercar i examinar etiquetes
  5. Etiquetar un commit del passat
  6. Versionat semàntic
  7. Enviar etiquetes al remot
  8. Esborrar etiquetes i per què no es mouen
  9. Etiquetes signades
  10. git describe: anomenar qualsevol commit
  11. Treballar sobre una etiqueta

  1. Què és una etiqueta i en què es diferencia d'una branca

Recorda la lliçó 03-01: una branca és un fitxer de 41 bytes a .git/refs/heads/ que conté un hash. Una etiqueta és el mateix, però a .git/refs/tags/:

git tag v1.0.0
cat .git/refs/tags/v1.0.0
9f4c2e8b7d1a5f3c6e9b2d8a4f7c1e5b3d9a6f2c

La diferència no és en el format sinó en el comportament:

Branca Etiqueta
On viu refs/heads/ refs/tags/
Es mou sola? : avança amb cada commit No: mai
HEAD li pot apuntar? No: et deixa en detached HEAD
Significat "Aquí treballo" "Això és un punt concret"
S'envia amb git push Sí (segons refspec) No per defecte
Pot tenir metadades pròpies No Sí, si és anotada

Aquest "no es mou sola" és tot el valor de l'eina. Si et situes a v1.0.0 d'aquí a tres anys, veus exactament el que hi havia quan es va publicar, encara que main hagi rebut mil commits des de llavors.

gitGraph
   commit id: "1a4c8d6"
   commit id: "8b6d3c2"
   commit id: "9f4c2e8" tag: "v1.0.0"
   commit id: "c5d9b1e"
   commit id: "7d3a8f4" tag: "v1.0.1"
   commit id: "e91d4a8"

main continua avançant cap a la dreta; v1.0.0 i v1.0.1 es queden clavades on es van posar.

  1. Etiquetes lleugeres davant d'anotades

Aquí hi ha el concepte central de la lliçó.

Una etiqueta lleugera (lightweight) és exactament el que acabes de veure: un fitxer amb un hash a dins. Un punter pelat, sense res més. No crea cap objecte nou a la base de dades.

Una etiqueta anotada (annotated) crea un objecte tag complet a la base de dades de Git, amb el seu propi hash, que conté: a quin commit apunta, qui la va crear, quan, un missatge, i opcionalment una signatura criptogràfica. I refs/tags/v1.0.0 apunta a aquest objecte, no al commit.

Vegem-ho amb les eines de 01-04:

# Etiqueta lleugera
git tag lleugera-1.0
git cat-file -t lleugera-1.0
commit

La referència resol directament a un commit: no hi ha objecte intermedi.

# Etiqueta anotada
git tag -a v1.0.0 -m "Primera versió estable de gestor-tasques"
git cat-file -t v1.0.0
tag
git cat-file -p v1.0.0
object 9f4c2e8b7d1a5f3c6e9b2d8a4f7c1e5b3d9a6f2c
type commit
tag v1.0.0
tagger Ana Ferrer <[email protected]> 1754035200 +0200

Primera versió estable de gestor-tasques

Inclou el llistat de tasques amb persistència a localStorage,
el comptador de pendents, el filtre i l'exportació a CSV.

Aquí hi ha el quart tipus d'objecte, per fi al seu hàbitat natural. Fixa't en l'estructura: object (a què apunta), type (què és allò apuntat), tag (el nom), tagger (qui i quan) i el missatge. I com tot objecte de Git, és immutable: el seu hash es calcula sobre aquest contingut.

La comparació completa:

Aspecte Lleugera Anotada
Com es crea git tag <nom> git tag -a <nom> -m "..."
Objecte a la base de dades Cap Un objecte tag
A què apunta refs/tags/<n> Al commit A l'objecte tag
Autor de l'etiqueta No es desa Sí (tagger)
Data de l'etiqueta No es desa
Missatge No
Es pot signar (GPG/SSH) No
Apareix a git describe Només amb --tags Sí, per defecte
--follow-tags l'envia No
Ús recomanat Marques locals i temporals Versions publicades, sempre

Per què per publicar versions es fan servir sempre les anotades, en quatre raons concretes:

  1. Deixen constància de qui i quan. Un v1.0.0 sense autor ni data no respon a "qui va decidir que això era la 1.0?".
  2. Porten un missatge. Aquest missatge és el lloc natural per a les notes de la versió, i viatja amb el repositori.
  3. Es poden signar. Una etiqueta signada permet verificar criptogràficament que la versió la va publicar qui diu.
  4. Git les tracta com a ciutadanes de primera. git describe les prefereix, push --follow-tags només envia aquestes, i diverses eines de l'ecosistema les esperen.

La regla pràctica és simple: etiqueta lleugera = marcador personal d'usar i llençar; etiqueta anotada = tota la resta.

  1. Crear etiquetes

# Lleugera, al commit actual
git tag proves-avui

# Anotada, al commit actual (la forma normal)
git tag -a v1.0.0 -m "Primera versió estable de gestor-tasques"

# Anotada amb missatge llarg: sense -m, s'obre l'editor
git tag -a v1.0.0

Quan s'obre l'editor, escriu un assumpte a la primera línia, una línia en blanc i el cos. És exactament la convenció dels missatges de commit (lliçó 08-01):

gestor-tasques 1.0.0

Primera versió estable, llesta per al desplegament del client.

Inclou:
- Alta, llistat i esborrat de tasques amb persistència a localStorage
- Comptador de tasques pendents
- Filtre de pendents
- Exportació del llistat a CSV

Un avís sobre els noms: són referències de Git, així que valen les mateixes regles que per a les branques (lliçó 03-06). Sense espais, sense .., sense ~, ^, :, ?, *, [, sense @{, sense acabar en . ni en .lock. La convenció aclaparadorament majoritària és v seguida del número: v1.0.0, v2.3.1.

I una precaució que evita un problema clàssic: no facis servir el mateix nom per a una branca i una etiqueta. Si existeixen v1.0.0 com a branca i com a etiqueta, git checkout v1.0.0 és ambigu, Git avisa i aplica una regla de precedència que ningú no recorda. Ho vam veure de passada a 04-05 en parlar de refspecs complets; la solució és no crear el problema.

  1. Llistar, cercar i examinar etiquetes

git tag
v0.9.0
v1.0.0
v1.0.1
v1.1.0
v1.10.0
v1.2.0

Ordenades alfabèticament, que és un ordre incorrecte per a versions: v1.10.0 surt abans que v1.2.0. Git ho sap i té una solució:

git tag --sort=-v:refname
v1.10.0
v1.2.0
v1.1.0
v1.0.1
v1.0.0
v0.9.0

v:refname ordena entenent la semàntica dels números de versió, i el - inverteix per posar la més nova a dalt. Val la pena deixar-ho configurat:

git config --global tag.sort -v:refname

Filtrar per patró amb -l (o --list), que accepta comodins de shell:

git tag -l "v1.0.*"
v1.0.0
v1.0.1
v1.0.2
git tag -l "v1.*" -l "v2.*"     # diversos patrons alhora

Veure la informació de cada etiqueta al llistat:

git tag -n            # la primera línia del missatge
git tag -n5           # les cinc primeres
v0.9.0          Versio previa a la primera estable
v1.0.0          gestor-tasques 1.0.0
v1.0.1          Correcció del focus després d'esborrar una tasca

Examinar una etiqueta a fons amb git show:

git show v1.0.0
tag v1.0.0
Tagger: Ana Ferrer <[email protected]>
Date:   Fri Aug 1 10:00:00 2026 +0200

gestor-tasques 1.0.0

Primera versió estable, llesta per al desplegament del client.

commit 9f4c2e8b7d1a5f3c6e9b2d8a4f7c1e5b3d9a6f2c
Author: Bruno Salas <[email protected]>
Date:   Thu Jul 31 18:22:41 2026 +0200

    Afegeix el botó d'exportar a la barra d'accions

diff --git a/index.html b/index.html
...

Amb una etiqueta anotada veus dos blocs: primer l'etiqueta (amb el seu tagger, la seva data i el seu missatge) i després el commit al qual apunta amb el seu diff. Amb una de lleugera només veuries el commit, perquè no hi ha res més per veure.

Altres consultes útils:

# Format personalitzat, com a git branch --format (lliçó 03-06)
git tag --format='%(refname:short) %(creatordate:short) %(subject)' --sort=-creatordate
v1.0.1 2026-08-05 Correcció del focus després d'esborrar una tasca
v1.0.0 2026-08-01 gestor-tasques 1.0.0
v0.9.0 2026-07-15 Versio previa a la primera estable
# Quines etiquetes contenen un commit (és a dir, en quines versions va entrar aquell canvi)
git tag --contains 7d3a8f4
v1.0.1
v1.1.0

Aquesta última és de les més útils del repertori: respon a "en quina versió es va corregir això?" sense obrir res.

# Què ha canviat entre dues versions
git log --oneline v1.0.0..v1.1.0
git diff --stat v1.0.0 v1.1.0

Les etiquetes funcionen com qualsevol altra referència en rangs, diff, log i show. Tot el que vas aprendre a la lliçó 02-06 val aquí.

  1. Etiquetar un commit del passat

És habitual adonar-se que cal etiquetar alguna cosa després d'haver continuat treballant. N'hi ha prou de passar el commit:

git tag -a v0.9.0 8b6d3c2 -m "Versio previa a la primera estable"
git show v0.9.0 | head -8
tag v0.9.0
Tagger: Ana Ferrer <[email protected]>
Date:   Fri Aug 1 10:14:33 2026 +0200

Versio previa a la primera estable

commit 8b6d3c2...

Un detall important que s'hi veu: la data de l'etiqueta és la d'avui, no la del commit. És correcte: l'etiqueta s'ha creat avui, encara que marqui alguna cosa de fa setmanes. Si per algun motiu necessites que coincideixi:

GIT_COMMITTER_DATE="2026-07-15T18:00:00" git tag -a v0.9.0 8b6d3c2 -m "..."

I com que el commit es pot indicar amb qualsevol expressió de les que coneixes (lliçó 02-06), tot això és vàlid:

git tag -a v0.9.0 HEAD~5 -m "..."
git tag -a v0.9.0 main~10 -m "..."
git tag -a v0.9.0 funcionalitat/exportacio-csv -m "..."

  1. Versionat semàntic

Etiquetar v1.0.0 no significa res si l'equip no comparteix què vol dir aquest número. El versionat semàntic (SemVer) és la convenció més estesa per donar-li significat.

Format: MAJOR.MINOR.PATCH

Component S'incrementa quan... Efecte en els altres
MAJOR Hi ha un canvi incompatible amb la versió anterior MINOR i PATCH tornen a 0
MINOR S'afegeix funcionalitat compatible cap enrere PATCH torna a 0
PATCH Es corregeix una fallada sense canviar el comportament esperat

Aplicat a la història real de gestor-tasques:

Versió Què va canviar Per què aquest número
v0.1.0 Llistat de tasques bàsic Abans de la 1.0 no hi ha compromís d'estabilitat
v0.9.0 Tot el previst, en proves Continua sent prèvia
v1.0.0 Primera versió estable S'assumeix el compromís de compatibilitat
v1.0.1 Corregit el focus després d'esborrar Només una correcció: PATCH
v1.1.0 Afegit el filtre de pendents Funcionalitat nova, res no es trenca: MINOR
v1.1.1 Corregit el comptador amb el filtre actiu PATCH
v1.2.0 Afegida l'exportació a CSV MINOR
v2.0.0 El format de localStorage canvia i les dades de la 1.x no es llegeixen Trenca la compatibilitat: MAJOR

Dues regles que sovint es passen per alt:

  • La versió 0.x.y és territori sense garanties. Abans de la 1.0.0, la convenció diu explícitament que qualsevol cosa pot canviar. És el lloc on ser mentre el disseny no estigui assentat.
  • Un cop publicada, una versió no es toca mai. Si v1.0.0 tenia una fallada, es publica v1.0.1. Mai no es reetiqueta v1.0.0 (apartat 8).

SemVer admet a més sufixos per a versions prèvies i metadades de compilació:

git tag -a v2.0.0-alpha.1 -m "Primera alfa de la versió 2"
git tag -a v2.0.0-rc.1 -m "Candidata a versió"
git tag -a v2.0.0 -m "gestor-tasques 2.0.0"

En l'ordre de SemVer, v2.0.0-alpha.1 < v2.0.0-rc.1 < v2.0.0: els sufixos de prellançament van abans que la versió final. git tag --sort=-v:refname ho entén correctament.

Com es decideix què entra a cada versió, qui l'aprova, com es generen les notes i com s'automatitza la publicació són temes de procés d'equip i d'integració contínua: els veuràs als mòduls 7 i 10. Aquí ens ocupa el mecanisme de Git.

  1. Enviar etiquetes al remot

A la lliçó 04-05 vam deixar això obert amb una frase que sorprèn a tothom: git push no envia etiquetes. Ara tanquem el tema.

El motiu és el refspec per defecte (lliçó 04-02): refs/heads/*:refs/heads/*. Només cobreix branques. refs/tags/* en queda fora.

git tag -a v1.0.0 -m "gestor-tasques 1.0.0"
git push
Everything up-to-date

L'etiqueta s'ha creat en local i allà continua. Les tres maneres d'enviar-la:

# 1. Una etiqueta concreta
git push origin v1.0.0

# 2. TOTES les etiquetes locals
git push origin --tags

# 3. Només les ANOTADES accessibles des del que s'envia
git push origin --follow-tags
Forma Què envia Quan
git push origin <etiqueta> Només aquesta En publicar una versió concreta: el més habitual
git push origin --tags Totes, lleugeres incloses, apuntin on apuntin Gairebé mai: arrossega els teus marcadors personals
git push origin --follow-tags Només les anotades que pengen dels commits enviats El dia a dia

--tags té un problema real: envia també proves-avui, abans-del-rebase i qualsevol marcador lleuger que tinguis per allà, i un cop al servidor són de tothom. Per això --follow-tags és gairebé sempre l'opció correcta, i per això convé deixar-la configurada:

git config --global push.followTags true

A partir d'aquí, un git push normal s'endú les etiquetes anotades que corresponguin i cap més.

Un matís sobre --follow-tags: només envia etiquetes anotades i només les que apunten a commits que ja són o seran al servidor. És exactament el que vols, i també una altra raó pràctica per fer servir sempre anotades en publicar versions.

En rebre, no cal fer res especial: git fetch i git pull porten les etiquetes dels commits que descarreguen. Si necessites portar-les totes explícitament:

git fetch --tags
git fetch --prune --prune-tags     # a més, esborra en local les que ja no són al servidor

  1. Esborrar etiquetes i per què no es mouen

Esborrar en local:

git tag -d proves-avui
Deleted tag 'proves-avui' (was 9f4c2e8)

Esborrar al remot (mateixa sintaxi d'esborrat de branques de 04-05):

git push origin --delete v1.0.0-rc.1
To git.exemple.cat:equip/gestor-tasques.git
 - [deleted]         v1.0.0-rc.1
# Forma antiga equivalent: enviar "res" a aquella referència
git push origin :refs/tags/v1.0.0-rc.1

I ara l'important: per què moure una etiqueta ja publicada és una mala idea?

Tècnicament es pot. git tag -f v1.0.0 <un-altre-commit> la reapunta, i git push --force origin v1.0.0 la reapunta al servidor. Però:

  1. Ningú no se n'assabenta. Qui ja tenia v1.0.0 descarregada no l'actualitza amb un git fetch normal. Git és deliberadament conservador amb les etiquetes existents: si el nom ja existeix en local, no el toca. Resultat: durant mesos, la teva v1.0.0 i la del Bruno apunten a commits diferents, i cap dels dos no ho sap.
  2. Trenca la premissa sencera. Una etiqueta val perquè és estable. Una etiqueta que pot canviar no serveix de res: ja no la pots citar en una incidència, en un desplegament ni en un document.
  3. Els artefactes ja publicats no canvien. Si v1.0.0 està desplegada en un client, moure l'etiqueta no mou el codi del client. Només aconsegueix que l'etiqueta menteixi sobre què es va desplegar.
  4. És la mateixa violació de la regla d'or del mòdul. Reescriure alguna cosa que altres ja tenen descarregada no és una operació local.

Qui rep una etiqueta moguda veu això, si la força:

git fetch --tags --force
 t [tag update]      v1.0.0     -> v1.0.0

I a partir d'aquí pot tenir dificultats per saber quina era la bona.

Què fer en el seu lloc:

Situació Solució correcta
T'has equivocat de commit i encara no l'has publicada Esborra-la en local i crea-la bé
L'has publicada fa cinc minuts i ningú no ha fet fetch Esborra-la en local i en remot, avisa l'equip, i crea-la de nou
La versió té una fallada Publica v1.0.1. No reetiquetis mai
T'has equivocat en el missatge Si és recent i avises, esborrar i recrear. Si no, deixa-ho: no compensa

En resum: l'etiqueta és un compromís. Un cop enviada al servidor, es tracta com a immutable.

  1. Etiquetes signades

Una etiqueta anotada pot portar una signatura criptogràfica que demostri qui la va crear:

# Signar amb GPG
git tag -s v1.0.0 -m "gestor-tasques 1.0.0"

# Verificar
git verify-tag v1.0.0
git tag -v v1.0.0
gpg: Signature made Fri Aug  1 10:00:00 2026 CEST
gpg:                using RSA key 4A7D2F8B...
gpg: Good signature from "Ana Ferrer <[email protected]>" [ultimate]

Per a què serveix a la pràctica: si algú distribueix gestor-tasques descarregant l'etiqueta v1.0.0 del servidor, la signatura li permet comprovar que aquella versió la va publicar realment l'Ana i no algú que va aconseguir accés al servidor.

Només funciona amb etiquetes anotades —una de lleugera no té on desar la signatura—, i requereix tenir configurada una clau (user.signingkey, gpg.format, tag.gpgSign). La configuració completa, incloses les signatures amb claus SSH i la signatura de commits, és a la lliçó 08-05: Bones Pràctiques de Seguretat. Aquí n'hi ha prou de saber que l'opció existeix i que és una raó més per fer servir anotades.

  1. git describe: anomenar qualsevol commit

Tens un commit qualsevol, enmig del desenvolupament, i vols un nom llegible per a ell. git describe en construeix un a partir de l'etiqueta més recent que l'abasta:

git describe
v1.1.0-14-g8c3e7f1

Es llegeix així:

Part Significat
v1.1.0 L'etiqueta anotada més recent accessible des d'aquest commit
14 Hi ha 14 commits des d'aquella etiqueta fins aquí
g8c3e7f1 El commit actual és 8c3e7f1 (la g significa "git")

Si ets exactament sobre una etiqueta, el nom és l'etiqueta a seques:

git switch --detach v1.1.0
git describe
v1.1.0

Opcions importants:

Opció Què fa
--tags Considera també les etiquetes lleugeres
--always Si no hi ha cap etiqueta, retorna el hash abreujat en lloc de fallar
--dirty Afegeix -dirty si el directori de treball té canvis sense confirmar
--abbrev=<n> Longitud del hash abreujat (--abbrev=0 l'omet: només el nom de l'etiqueta)
--match "<patró>" Només etiquetes que casin amb el patró
--contains A l'inrevés: la primera etiqueta que conté aquest commit

La combinació que es fa servir a la pràctica per versionar compilacions:

git describe --tags --always --dirty
v1.1.0-14-g8c3e7f1-dirty

Aquest identificador és extraordinàriament útil: s'incrusta a l'aplicació i, quan algú reporta una fallada des d'una versió intermèdia, saps exactament de quin commit parles i si venia d'un directori brut. Per exemple, a gestor-tasques:

// Aquest valor l'injecta l'script de compilació amb la sortida de:
//   git describe --tags --always --dirty
const VERSIO = 'v1.1.0-14-g8c3e7f1';

function pintaPeu() {
  const peu = document.getElementById('peu');
  peu.textContent = 'gestor-tasques ' + VERSIO;
}

I --contains, per a la pregunta inversa:

git describe --contains 7d3a8f4
v1.0.1~2

"Aquell commit és dos commits abans de v1.0.1", és a dir: va entrar a la versió 1.0.1.

  1. Treballar sobre una etiqueta

Situar-se en una etiqueta et deixa en detached HEAD (lliçó 03-02), perquè una etiqueta no és una branca i no pot avançar:

git switch --detach v1.0.0
Note: switching to 'v1.0.0'.

You are in 'detached HEAD' state...
HEAD is now at 9f4c2e8 Afegeix el botó d'exportar a la barra d'accions

Això està bé per mirar: inspeccionar el codi d'aquella versió, executar-la, reproduir una fallada. Però si has de treballar —per exemple, corregir una fallada de la 1.0 per publicar una 1.0.1— necessites una branca:

git switch -c manteniment/1.0 v1.0.0
Switched to a new branch 'manteniment/1.0'

Ara sí: branca de manteniment que arrenca exactament a la versió publicada. Corregeixes (o portes la correcció des de main amb git cherry-pick -x, lliçó 05-03), confirmes, etiquetes la nova versió i l'envies:

git cherry-pick -x 7d3a8f4
git tag -a v1.0.1 -m "Correcció del focus després d'esborrar una tasca"
git push origin manteniment/1.0 v1.0.1

Aquest és el cicle complet d'una versió de manteniment, i fa servir quatre coses d'aquest mòdul alhora.

També pots exportar el contingut d'una etiqueta sense clonar res:

git archive --format=zip --prefix=gestor-tasques-1.0.0/ v1.0.0 -o gestor-tasques-1.0.0.zip

git archive genera un .zip (o .tar.gz) amb l'arbre d'aquella etiqueta, sense el directori .git. És la manera canònica de produir el paquet d'una versió.

Errors Habituals i Consells

Error 1: crear etiquetes lleugeres per a versions. git tag v1.0.0 sense -a no desa ni qui, ni quan, ni per què, no es pot signar i --follow-tags no l'envia. Per a versions, sempre -a.

Error 2: creure que git push envia les etiquetes. No ho fa. És la sorpresa clàssica: crees v1.0.0, fas push, i al servidor no hi és. git push origin v1.0.0 o push.followTags true.

Error 3: fer servir --tags per costum. Envia totes les teves etiquetes locals, inclosos els marcadors de proves. Un cop al servidor, netejar-les és empipador i cal avisar tothom.

Error 4: moure una etiqueta publicada. Qui ja la tenia no l'actualitza i acabeu amb dues veritats diferents. Si hi ha una fallada, es publica la versió següent.

Error 5: confiar en l'ordre alfabètic. v1.10.0 va abans que v1.2.0 en ordre alfabètic. Configura tag.sort -v:refname.

Error 6: fer servir el mateix nom per a una branca i una etiqueta. Genera ambigüitats a checkout, switch i push que després costa entendre.

Error 7: esperar que git describe vegi les lleugeres. Per defecte només mira les anotades. Si el teu repositori només té lleugeres, git describe falla amb no names found; allà cal --tags.

Consell 1: etiqueta des de main i només el que es publica. Una etiqueta per versió real. Etiquetar commits intermedis "per si de cas" omple l'espai de noms de soroll permanent.

Consell 2: escriu un missatge de debò. El missatge de l'etiqueta és el millor lloc per a les notes de la versió: viatja amb el repositori, no depèn de cap eina externa i es llegeix amb git show.

Consell 3: incrusta git describe --tags --always --dirty a les teves compilacions. És la manera més barata que qualsevol informe d'error inclogui la versió exacta.

Consell 4: git tag --contains <sha> per respondre "en quina versió va entrar això?". És més ràpid i més fiable que buscar al log per dates.

Consell 5: acorda SemVer amb l'equip i escriu-ho. El valor de MAJOR.MINOR.PATCH rau en el fet que tothom entengui el mateix. Un número de versió que ningú no sap interpretar és només un número.

Exercicis

Exercici 1: lleugera davant d'anotada, amb les mans

En un repositori de proves amb almenys tres commits:

  1. Crea una etiqueta lleugera i una d'anotada sobre el mateix commit.
  2. Demostra amb git cat-file -t que una resol a commit i l'altra a tag.
  3. Mostra el contingut de l'objecte tag amb git cat-file -p i identifica'n les cinc parts.
  4. Compara la sortida de git show amb cadascuna.

Exercici 2: una història de versions

Simula l'evolució de gestor-tasques:

  1. Tres commits i v1.0.0 (anotada, amb missatge de notes de versió).
  2. Un commit de correcció i v1.0.1.
  3. Dos commits de funcionalitat nova i v1.1.0.
  4. Llista les etiquetes ordenades per versió, de la més nova a la més antiga.
  5. Mostra què va canviar entre v1.0.0 i v1.1.0.
  6. Esbrina en quina versió va entrar el commit de correcció sense mirar el log.

Exercici 3: git describe i una branca de manteniment

Partint de l'exercici 2:

  1. Afegeix quatre commits més a main i comprova la sortida de git describe. Interpreta'n cada part.
  2. Modifica un fitxer sense confirmar i observa l'efecte de --dirty.
  3. Crea una branca de manteniment que arrenqui a v1.0.0.
  4. Porta a aquella branca, amb cherry-pick, la correcció de la 1.0.1, i etiqueta el resultat com a v1.0.2.
  5. Comprova amb git describe a cada branca que els noms són coherents.

Solucions

Solució 1:

mkdir /tmp/practica-tags && cd /tmp/practica-tags
git init -b main
echo "un" > f.txt && git add . && git commit -m "Primer"
echo "dos" >> f.txt && git commit -am "Segon"
echo "tres" >> f.txt && git commit -am "Tercer"

# 1. Les dues etiquetes
git tag lleugera
git tag -a anotada -m "Aquesta és anotada"
# 2. El tipus d'objecte al qual resolen
git cat-file -t lleugera
git cat-file -t anotada
commit
tag
# 3. L'interior de l'objecte tag
git cat-file -p anotada
object 6d3f2a9c8b1e5f7d4a2c9e6b3f8d1a5c7e4b2f9d
type commit
tag anotada
tagger Carla Vidal <[email protected]> 1754035200 +0200

Aquesta és anotada

Les cinc parts: object (el commit apuntat), type (què és), tag (el nom), tagger (autoria i data) i el missatge.

# 4. git show amb cadascuna
git show lleugera | head -4
git show anotada | head -8
commit 6d3f2a9...
Author: Carla Vidal <[email protected]>
Date:   Sat Aug 1 11:02:14 2026 +0200
tag anotada
Tagger: Carla Vidal <[email protected]>
Date:   Sat Aug 1 11:03:40 2026 +0200

Aquesta és anotada

commit 6d3f2a9...

L'anotada mostra un bloc més: el seu propi.

Solució 2:

mkdir /tmp/practica-versions && cd /tmp/practica-versions
git init -b main

echo "<h1>Gestor de tasques</h1>" > index.html && git add . && git commit -m "Estructura inicial"
echo "body { font-family: sans-serif; }" > estils.css && git add . && git commit -m "Afegeix els estils base"
echo "const tasques = [];" > app.js && git add . && git commit -m "Afegeix el llistat de tasques"

git tag -a v1.0.0 -m "gestor-tasques 1.0.0

Primera versió estable: llistat de tasques amb estils base."
echo "// focus després d'esborrar" >> app.js && git commit -am "Retorna el focus al camp després d'esborrar"
git tag -a v1.0.1 -m "Correcció del focus després d'esborrar una tasca"

echo "// filtre" >> app.js && git commit -am "Afegeix el filtre de pendents"
echo ".filtre { margin: 1rem; }" >> estils.css && git commit -am "Afegeix els estils del filtre"
git tag -a v1.1.0 -m "gestor-tasques 1.1.0

Nou filtre de tasques pendents."
# 4. Ordenades per versió, la més nova a dalt
git tag --sort=-v:refname -n1
v1.1.0          gestor-tasques 1.1.0
v1.0.1          Correcció del focus després d'esborrar una tasca
v1.0.0          gestor-tasques 1.0.0
# 5. Què va canviar entre versions
git log --oneline v1.0.0..v1.1.0
git diff --stat v1.0.0 v1.1.0
2c9f4e7 Afegeix els estils del filtre
8b1d6a3 Afegeix el filtre de pendents
5f7c2e9 Retorna el focus al camp després d'esborrar
 app.js     | 2 ++
 estils.css | 1 +
 2 files changed, 3 insertions(+)
# 6. En quina versió va entrar la correcció
git tag --contains 5f7c2e9
v1.0.1
v1.1.0

Va entrar a la v1.0.1 (la primera de la llista) i, naturalment, continua present a la v1.1.0.

Solució 3:

# 1. Quatre commits més i describe
for i in 1 2 3 4; do echo "// canvi $i" >> app.js; git commit -am "Canvi $i"; done
git describe
v1.1.0-4-g9e2c6b1

L'etiqueta anotada més recent accessible és v1.1.0, han passat 4 commits des d'ella, i el commit actual és 9e2c6b1.

# 2. --dirty
echo "sense confirmar" >> app.js
git describe --dirty
v1.1.0-4-g9e2c6b1-dirty
git checkout -- app.js     # deixar-ho net una altra vegada
# 3 i 4. Branca de manteniment i cherry-pick
git switch -c manteniment/1.0 v1.0.0
git log --oneline -1
5c8e1f4 Afegeix el llistat de tasques
git cherry-pick -x 5f7c2e9
git tag -a v1.0.2 -m "Porta la correcció del focus a la branca 1.0"
# 5. describe a cada branca
git describe
git switch main
git describe
v1.0.2
v1.1.0-4-g9e2c6b1

Cada branca s'anomena respecte a la seva pròpia línia d'etiquetes, que és exactament el que s'espera d'un esquema de versions amb manteniment.

Conclusió

Les etiquetes són la memòria estable del repositori. L'essencial:

  • Una etiqueta és una referència que no es mou. Viu a refs/tags/, marca un commit concret per sempre i no pot ser la destinació de HEAD (et deixa en detached HEAD).
  • Hi ha dues classes. La lleugera és un punter pelat, sense objecte propi. L'anotada crea un objecte tag a la base de dades —el quart tipus de la lliçó 01-04— amb autor, data, missatge i signatura opcional. Per a versions publicades, sempre anotades.
  • Es creen amb git tag -a <nom> -m "...", opcionalment sobre un commit del passat; es llisten amb git tag -l "<patró>", -n i --sort=-v:refname; s'examinen amb git show; i git tag --contains <sha> respon a "en quina versió va entrar això?".
  • El versionat semàntic (MAJOR.MINOR.PATCH) dona significat compartit al número: incompatible / funcionalitat nova / correcció. Abans de la 1.0.0 no hi ha compromís; després, una versió publicada no es toca mai.
  • Les etiquetes no viatgen soles: git push origin <etiqueta> per a una de concreta, --tags per a totes (poques vegades el que vols) i --follow-tags —o push.followTags true— per a les anotades accessibles, que és el correcte en el dia a dia.
  • S'esborren amb git tag -d en local i git push origin --delete en remot, però una etiqueta publicada no es mou: qui ja la té no l'actualitza, i el resultat són dues veritats diferents convivint. Si hi ha una fallada, es publica la versió següent.
  • git describe --tags --always --dirty anomena qualsevol commit respecte a l'última etiqueta i és la manera canònica de versionar compilacions.
  • Per treballar sobre una versió publicada, crea una branca des de l'etiqueta; l'etiqueta sola només serveix per mirar.

El que ve

Ja sabem marcar el passat. Falta l'operació complementària i la més delicada de totes: desfer-lo.

Perquè passarà. Algú publicarà a main un commit que trenca alguna cosa, i ja serà al servidor, i tres persones el tindran descarregat. La regla d'or prohibeix reescriure'l. Llavors?

Git té una resposta per a això, i és elegant: no esborrar el commit, sinó afegir-ne un altre que apliqui el canvi contrari. És git revert, l'única manera segura de desfer alguna cosa que ja és pública, i amb ella tanquem el mòdul a la lliçó 05-06: Revertint Confirmacions.

Dominant Git: De Principiant a Avançat

Mòdul 1: Introducció a Git

Mòdul 2: Operacions Bàsiques de Git

Mòdul 3: Branques i Fusió

Mòdul 4: Treballant amb Repositoris Remots

Mòdul 5: Operacions Avançades de Git

Mòdul 6: Eines i Tècniques de Git

Mòdul 7: Estratègies de Col·laboració i Flux de Treball

Mòdul 8: Bones Pràctiques i Consells de Git

Mòdul 9: Resolució de Problemes i Depuració

Mòdul 10: Git al Món Real

© Copyright 2026. Tots els drets reservats