Tags et releases#

Les tags sont des marqueurs que l’on pose sur des points précis de l’historique pour les identifier de manière permanente. Ils servent typiquement à repérer les versions publiées d’un logiciel – v1.0, v2.3.1, etc. – mais aussi tout jalon significatif du projet. Contrairement aux branches, qui avancent automatiquement à chaque nouveau commit, un tag reste figé sur le commit qu’il désigne. C’est cette immobilité qui fait sa valeur : un tag est une référence stable et immuable.

Ce chapitre couvre les deux types de tags proposés par Git, les commandes de gestion associées, le versionnement sémantique (SemVer), la commande git describe et le lien entre tags et releases sur les plateformes d’hébergement.

Tags légers vs annotés#

Définition 52 (Tag léger (lightweight tag))

Un tag léger est un simple pointeur vers un commit, exactement comme une branche qui ne bougerait jamais. Il ne contient aucune métadonnée supplémentaire : ni auteur, ni date, ni message. Concrètement, c’est un fichier dans .git/refs/tags/ qui contient le hash SHA-1 du commit cible.

On le crée avec :

git tag v1.0

Définition 53 (Tag annoté (annotated tag))

Un tag annoté est un objet Git à part entière, stocké dans la base d’objets au même titre qu’un commit, un tree ou un blob. Il contient le nom du créateur (tagger), son adresse email, la date de création et un message descriptif. Il peut également être signé cryptographiquement avec GPG.

On le crée avec :

git tag -a v1.0 -m "Version 1.0"

Remarque 52

Pour les releases et tout tag destiné à être partagé, il faut toujours utiliser des tags annotés. Les métadonnées qu’ils contiennent (auteur, date, message) constituent une trace précieuse. Les tags légers conviennent aux marqueurs temporaires ou privés, par exemple pour repérer un commit lors d’une session de débogage.

Créons un dépôt de démonstration et comparons les deux types de tags.

%%bash
cd /tmp && rm -rf demo-tags && mkdir demo-tags && cd demo-tags && git init
git config user.email "demo@example.com" && git config user.name "Demo"

# Quelques commits pour construire un historique
echo "# Mon Projet" > README.md
git add README.md && git commit -m "Initial commit"

echo "print('hello')" > main.py
git add main.py && git commit -m "Ajouter main.py"

echo "print('feature A')" > feature_a.py
git add feature_a.py && git commit -m "Ajouter feature A"

echo "fix = True" >> main.py
git add main.py && git commit -m "Corriger un bug dans main.py"

echo "print('feature B')" > feature_b.py
git add feature_b.py && git commit -m "Ajouter feature B"
Dépôt Git vide initialisé dans /tmp/demo-tags/.git/
[main (commit racine) f611fa9] Initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 RE
ADME.md
[main 57cb360] Ajouter main.py
 1 file changed, 1 insertion(+)
 create mode 100644 main.py
[main 9303925] Ajouter feature A
 1 file changed, 1 insertion(+)
 create mode 100644 feature_a.py
[main ecac687] Corriger un bug dans main.py
 1 file changed, 1 insertion(+)
[main 82b82b8] Ajouter feature B
 1 file changed, 1 insertion(+)
 create mode 100644 feature_b.py
%%bash
cd /tmp/demo-tags

# Créer un tag léger
git tag v0.1

# Créer un tag annoté
git tag -a v1.0 -m "Première version stable"

echo "=== Type de l'objet pointé par le tag léger (v0.1) ==="
git cat-file -t v0.1

echo ""
echo "=== Type de l'objet pointé par le tag annoté (v1.0) ==="
git cat-file -t v1.0
=== Type de l'objet pointé par le tag léger (v0.1) ===
commit
=== Type de l'objet pointé par le tag annoté (v1.0) ===
tag

Le tag léger pointe directement vers un objet commit, tandis que le tag annoté pointe vers un objet tag – un objet intermédiaire qui contient les métadonnées. Comparons avec git show :

%%bash
cd /tmp/demo-tags

echo "=== git show v0.1 (tag léger) ==="
git show v0.1 --quiet

echo ""
echo "=== git show v1.0 (tag annoté) ==="
git show v1.0 --quiet
=== git show v0.1 (tag léger) ===
commit 82b82b811051df23bbba8ced0029ede39205065e
Author: Demo <demo@example.com>
Date:   Sun Mar 15 2
1:25:45 2026 +0100

    Ajouter feature B
=== git show v1.0 (tag annoté) ===
tag v1.0
Tagger: Demo <demo@example.com>
Date:   Sun Mar 15 21:25:45 2026 +0100

Première version s
table

commit 82b82b811051df23bbba8ced0029ede39205065e
Author: Demo <demo@example.com>
Date:   Sun M
ar 15 21:25:45 2026 +0100

    Ajouter feature B

Gestion des tags#

Lister et filtrer les tags#

%%bash
cd /tmp/demo-tags

# Ajouter quelques tags supplémentaires pour la démonstration
git tag -a v1.1 -m "Version 1.1 - corrections mineures"
git tag -a v2.0-beta.1 -m "Version 2.0 beta 1"

echo "=== Tags correspondant à v1.* ==="
git tag -l "v1.*"

echo ""
echo "=== Tous les tags ==="
git tag
=== Tags correspondant à v1.* ===
v1.0
v1.1
=== Tous les tags ===
v0.1
v1.0
v1.1
v2.0-beta.1

Tagger un commit passé#

On peut tagger n’importe quel commit de l’historique en spécifiant son hash.

%%bash
cd /tmp/demo-tags

# Retrouver le hash du deuxième commit
HASH=$(git log --oneline | tail -2 | head -1 | cut -d' ' -f1)
echo "Hash du commit à tagger : $HASH"

# Tagger ce commit passé
git tag -a v0.0.1 "$HASH" -m "Toute première version fonctionnelle"

echo ""
echo "=== Vérification ==="
git log --oneline --decorate
Hash du commit à tagger : 57cb360

=== Vérification ===
82b82b8 (HEAD -> main, tag: v2.0-beta.1, tag: v1.1, tag: v1.0, tag: v0.1) Ajouter feature B
ecac687 
Corriger un bug dans main.py
9303925 Ajouter feature A
57cb360 (tag: v0.0.1) Ajouter main.py
f611fa9 Initial commit

Supprimer un tag localement#

%%bash
cd /tmp/demo-tags

echo "=== Avant suppression ==="
git tag

git tag -d v0.1
echo ""
echo "=== Après suppression de v0.1 ==="
git tag
=== Avant suppression ===
v0.0.1
v0.1
v1.0
v1.1
v2.0-beta.1
Étiquette 'v0.1' supprimée (elle était sur 82b82b8)
=== Après suppression de v0.1 ===
v0.0.1
v1.0
v1.1
v2.0-beta.1

Pousser les tags vers un dépôt distant#

Par défaut, git push ne transmet pas les tags. Il faut les pousser explicitement.

Remarque 53

Les commandes de gestion des tags distants sont les suivantes :

  • git push origin v1.0 – pousser un tag spécifique vers le dépôt distant.

  • git push origin --tags – pousser tous les tags locaux vers le dépôt distant.

  • git push origin :refs/tags/v1.0 – supprimer un tag sur le dépôt distant (la syntaxe :ref signifie « pousser rien vers cette référence », ce qui la supprime).

Depuis Git 2.17, on peut également utiliser git push origin --delete v1.0 pour supprimer un tag distant, syntaxe plus lisible que la forme avec :refs/tags/.

Versionnement sémantique (SemVer)#

Définition 54 (Versionnement sémantique (SemVer))

Le versionnement sémantique (Semantic Versioning, abrege SemVer) est une convention de nommage des versions sous la forme MAJOR.MINOR.PATCH, où chaque composante a une signification précise :

  • MAJOR : incrémente lors de changements incompatibles avec les versions précédentes (breaking changes). Les utilisateurs devront potentiellement adapter leur code.

  • MINOR : incrémente lors de l’ajout de nouvelles fonctionnalités qui restent rétrocompatibles. Le code existant continue de fonctionner.

  • PATCH : incrémente lors de corrections de bugs rétrocompatibles. Aucune fonctionnalité nouvelle, uniquement des réparations.

Par exemple, 2.3.7 signifie la 2e génération majeure, la 3e version mineure de cette génération, et le 7e correctif de cette version mineure.

Voici comment les numéros de version évoluent en pratique :

  • 1.0.01.0.1 : correction d’un bug (PATCH)

  • 1.0.11.1.0 : ajout d’une fonctionnalité (MINOR), le compteur PATCH revient à 0

  • 1.1.02.0.0 : changement incompatible (MAJOR), MINOR et PATCH reviennent à 0

Pour les versions en cours de développement, SemVer prévoit des suffixes de pre-release :

  • 2.0.0-alpha.1 : version alpha (fonctionnalités incomplêtes)

  • 2.0.0-beta.1 : version beta (fonctionnalités complêtes, tests en cours)

  • 2.0.0-rc.1 : release candidate (derniers tests avant publication)

Remarque 54

SemVer est une convention, pas un mécanisme imposé par Git. Rien n’empèche de nommer un tag banane-42 si on le souhaite. Mais suivre SemVer rend le versionnement prévisible pour les utilisateurs et les outils : un gestionnaire de paquets comme pip ou npm peut déterminer automatiquement si une mise à jour est sûre en se basant sur les numéros de version.

Visualisation : progression des versions#

Hide code cell source

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

fig, ax = plt.subplots(figsize=(14, 4.5))

versions = [
    ("1.0.0", 0, "major"), ("1.0.1", 1, "patch"), ("1.0.2", 2, "patch"),
    ("1.1.0", 3, "minor"), ("1.1.1", 4, "patch"), ("1.2.0", 5, "minor"),
    ("2.0.0-beta.1", 6, "pre"), ("2.0.0-rc.1", 7, "pre"),
    ("2.0.0", 8, "major"), ("2.0.1", 9, "patch"), ("2.1.0", 10, "minor"),
]

colors = {"major": "#d9534f", "minor": "#337ab7", "patch": "#5cb85c", "pre": "#f0ad4e"}
y = 0.5

for label, x, vtype in versions:
    c = colors[vtype]
    ax.plot(x, y, "o", markersize=16, color=c, zorder=5,
            markeredgecolor="white", markeredgewidth=2)
    ax.annotate(label, xy=(x, y), xytext=(x, y + 0.32),
                fontsize=8, fontweight="bold", ha="center", va="bottom",
                rotation=35, color=c)

ax.plot([v[1] for v in versions], [y] * len(versions),
        "-", color="#cccccc", linewidth=2, zorder=1)

legend_elements = [
    mpatches.Patch(facecolor=colors["major"], edgecolor="white", label="MAJOR (breaking change)"),
    mpatches.Patch(facecolor=colors["minor"], edgecolor="white", label="MINOR (nouvelle fonctionnalite)"),
    mpatches.Patch(facecolor=colors["patch"], edgecolor="white", label="PATCH (correction de bug)"),
    mpatches.Patch(facecolor=colors["pre"], edgecolor="white", label="Pre-release (alpha, beta, rc)"),
]
ax.legend(handles=legend_elements, loc="upper left", fontsize=9, frameon=True, fancybox=True)
ax.set_title("Progression des versions selon SemVer", fontsize=14, fontweight="bold", pad=15)
ax.set_xlim(-0.8, 11)
ax.set_ylim(-0.1, 1.4)
ax.axis("off")
plt.show()
_images/f780846f83be13e1991cb3258fd7e7603e07d14db62b50fdad9c9800ab17fa95.png

Git describe#

Définition 55 (git describe)

La commande git describe identifie le commit courant par rapport au tag annoté le plus proche qui lui est accessible dans l’historique. Elle produit un identifiant de la forme :

v1.2.3-5-gabcdef

où :

  • v1.2.3 est le nom du tag le plus proche,

  • 5 est le nombre de commits effectués depuis ce tag,

  • gabcdef est le hash abrégé du commit courant (préfixé par g pour git).

Si le commit courant porte exactement un tag, la commande retourne simplement le nom du tag (par exemple v1.2.3).

Cette commande est particulièrement utile pour le versionnement automatique des builds : elle génère un identifiant unique et lisible qui situe le code par rapport à la dernière version publiée.

%%bash
cd /tmp/demo-tags

echo "=== git describe sur le commit actuel (qui porte un tag) ==="
git describe

echo ""
# Ajouter quelques commits après le dernier tag
echo "nouveau code" > nouveau.py
git add nouveau.py && git commit -m "Ajouter nouveau.py"

echo "encore du code" >> nouveau.py
git add nouveau.py && git commit -m "Améliorer nouveau.py"

echo "=== git describe après deux commits supplémentaires ==="
git describe

echo ""
echo "=== git describe --long (format long, même si on est sur un tag) ==="
git describe --long
=== git describe sur le commit actuel (qui porte un tag) ===
v1.0

[main b74a3b6] Ajouter nouveau.py
 1 file changed, 1 insertion(+)
 create mode 100644 nouveau.py
[main 90ca3d9] Améliorer nouveau.py
 1 file changed, 1 insertion(+)
=== git describe après deux commits supplémentaires ===
v1.0-2-g90ca3d9
=== git describe --long (format long, même si on est sur un tag) ===
v1.0-2-g90ca3d9

Remarque 55

Par défaut, git describe ne considère que les tags annotés. Pour inclure également les tags légers dans la recherche, il faut ajouter l’option --tags. C’est une raison supplémentaire de privilégier les tags annotés pour les releases : ils sont reconnus par git describe sans option supplémentaire.

Releases sur GitHub et GitLab#

Sur les plateformes comme GitHub ou GitLab, les releases sont construites à partir des tags. La plateforme ajoute au tag des fonctionnalités supplémentaires : notes de version en Markdown, artefacts binaires à télécharger, et marquage pre-release ou latest. Pour préparer les notes de version, on génère typiquement la liste des commits entre deux tags.

%%bash
cd /tmp/demo-tags

echo "=== Commits entre v1.0 et v2.0-beta.1 ==="
git log --oneline v1.0..v2.0-beta.1
=== Commits entre v1.0 et v2.0-beta.1 ===

Remarque 56

Pour des changelogs plus élaborés, on peut enrichir la commande :

  • git log --oneline --no-merges v1.0..v2.0 pour exclure les commits de fusion.

  • git log --format="- %s (%h)" v1.0..v2.0 pour produire une liste à puces avec messages et hashs abrégés.

  • git shortlog -sne v1.0..v2.0 pour un résumé par auteur, utile pour les crédits.

Adopter une convention de messages de commit (comme Conventional Commits) permet la génération automatique de changelogs structurés.

Exemple 16 (Workflow typique de release)

Les étapes habituelles pour publier une version sont :

  1. S’assurer que tous les tests passent sur la branche main.

  2. Créer un tag annoté : git tag -a v1.2.0 -m "Version 1.2.0".

  3. Pousser le tag : git push origin v1.2.0.

  4. Sur GitHub/GitLab, créer une release à partir du tag avec les notes de version.

Ce workflow peut être automatisé avec des pipelines CI/CD qui détectent la création d’un tag et déclenchent la publication.

Résumé#

Le tableau suivant récapitule les principales commandes liées aux tags.

Commande

Description

git tag v1.0

Créer un tag léger sur le commit courant

git tag -a v1.0 -m "msg"

Créer un tag annoté avec un message

git tag

Lister tous les tags

git tag -l "v1.*"

Filtrer les tags par motif glob

git tag -a v1.0 <hash>

Tagger un commit passé

git tag -d v1.0

Supprimer un tag localement

git push origin v1.0

Pousser un tag spécifique vers le distant

git push origin --tags

Pousser tous les tags vers le distant

git push origin :refs/tags/v1.0

Supprimer un tag sur le distant

git show v1.0

Afficher les détails d’un tag

git describe

Identifier le commit par rapport au tag le plus proche

git describe --tags

Idem, en incluant les tags légers

git log --oneline v1.0..v2.0

Lister les commits entre deux tags