TP6 — Git Avancé
Travaux Pratiques 60 min Module 05
Objectifs
À la fin de ce TP, vous aurez pratiqué :
- Créer des tags annotés et des releases GitHub
- Utiliser
git cherry-pickpour des corrections ciblées - Utiliser
git bisectpour trouver un bug - Créer des hooks Git fonctionnels
- Utiliser
git reflogpour récupérer du travail perdu
Mise en place
gh repo create git-tp6-avance \
--public \
--description "TP6 - Techniques Git Avancées" \
--clone
cd git-tp6-avance
Créez app.py :
"""Application de gestion de tâches."""
TACHES = []
def ajouter_tache(titre, priorite="normale"):
tache = {"id": len(TACHES) + 1, "titre": titre, "priorite": priorite, "terminee": False}
TACHES.append(tache)
return tache
def marquer_terminee(id_tache):
for tache in TACHES:
if tache["id"] == id_tache:
tache["terminee"] = True
return tache
raise ValueError(f"Tâche {id_tache} non trouvée")
def obtenir_statistiques():
total = len(TACHES)
terminees = sum(1 for t in TACHES if t["terminee"])
return {
"total": total,
"terminees": terminees,
"en_cours": total - terminees
}
git add .
git commit -m "feat: application de gestion de tâches initiale"
git push -u origin main
Partie 1 : Tags et Releases
Créer l'historique des versions
# Version 1.0.0
git tag -a v1.0.0 -m "Release v1.0.0 — Première version stable"
git push origin v1.0.0
# Ajouter une nouvelle fonctionnalité
Ajoutez à app.py :
def filtrer_taches(priorite=None, terminee=None):
"""Filtrer les tâches par priorité ou statut."""
resultats = TACHES.copy()
if priorite:
resultats = [t for t in resultats if t["priorite"] == priorite]
if terminee is not None:
resultats = [t for t in resultats if t["terminee"] == terminee]
return resultats
git add app.py
git commit -m "feat: ajouter le filtrage des tâches"
git push
# Version 1.1.0
git tag -a v1.1.0 -m "Release v1.1.0 — Filtrage des tâches"
git push origin v1.1.0
Créer des releases GitHub
# Créer la release v1.0.0
gh release create v1.0.0 \
--title "Version 1.0.0 — Première release stable" \
--notes "## Première release stable
### Fonctionnalités
- Ajouter des tâches avec priorité
- Marquer les tâches comme terminées
- Obtenir les statistiques de l'équipe"
# Créer la release v1.1.0
gh release create v1.1.0 \
--title "Version 1.1.0 — Filtrage avancé" \
--generate-notes
# Voir les releases
gh release list
Partie 2 : Cherry-pick
Scénario : Correction urgente sur la branche release
# Créer une branche de release
git switch -c release/v1.0
git push -u origin release/v1.0
# Simuler un développement sur main
git switch main
Ajoutez à app.py :
def rechercher_taches(query):
"""Rechercher des tâches par titre."""
return [t for t in TACHES if query.lower() in t["titre"].lower()]
git add app.py
git commit -m "feat: ajouter la recherche de tâches"
# Simuler une correction de bug critique
echo "# Correction critique validée" >> README.md
git add README.md
git commit -m "fix: corriger la gestion des tâches avec ID dupliqués"
# Récupérer le hash de la correction
git log --oneline
# Cherry-pick de la correction vers release/v1.0
HASH_FIX=$(git log --oneline | head -1 | awk '{print $1}')
git switch release/v1.0
git cherry-pick $HASH_FIX
# Vérifier que la correction est appliquée
git log --oneline
git switch main
Partie 3 : Git bisect
Créer un historique avec un bug introduit
git switch main
# Commit 1 : Fonctionnel
git commit --allow-empty -m "chore: version v1.2 - base saine"
git tag v1.2.0
# Commit 2 : Toujours fonctionnel
git commit --allow-empty -m "feat: amélioration mineure A"
# Commit 3 : Introduction du bug (dans app.py)
Modifiez obtenir_statistiques dans app.py pour introduire un bug :
def obtenir_statistiques():
total = len(TACHES)
terminees = sum(1 for t in TACHES if t["terminee"])
return {
"total": total,
"terminees": terminees,
"en_cours": total - terminees,
"pourcentage": terminees / total * 100 # BUG: division par zéro si TACHES est vide !
}
git add app.py
git commit -m "feat: ajouter le pourcentage aux statistiques"
# Commit 4 : Bug non détecté
git commit --allow-empty -m "feat: amélioration mineure B"
# Commit 5 : Bug toujours présent
git commit --allow-empty -m "docs: mise à jour de la documentation"
git push
Trouver le bug avec bisect
# Créer un script de test
cat > test_bisect.sh << 'EOF'
#!/bin/bash
python -c "
import sys
sys.path.insert(0, '.')
from app import obtenir_statistiques, TACHES
TACHES.clear()
try:
stats = obtenir_statistiques()
exit(0)
except (ZeroDivisionError, KeyError):
exit(1)
"
EOF
chmod +x test_bisect.sh
# Lancer bisect
git bisect start
git bisect bad HEAD
git bisect good v1.2.0
git bisect run ./test_bisect.sh
# Git trouve le commit coupable !
# Terminer bisect
git bisect reset
Partie 4 : Créer des hooks Git
mkdir -p .githooks
Créez .githooks/commit-msg :
#!/bin/bash
FICHIER_MSG="$1"
MESSAGE=$(cat "$FICHIER_MSG")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|revert)(\(.+\))?: .+"
if ! echo "$MESSAGE" | grep -qE "$PATTERN"; then
echo "ERREUR : Message de commit invalide !"
echo "Format requis : type(portée): description"
echo "Types valides : feat, fix, docs, style, refactor, test, chore, perf, ci, revert"
echo "Votre message : $MESSAGE"
exit 1
fi
exit 0
Créez .githooks/pre-commit :
#!/bin/bash
echo "Vérification pre-commit..."
# Vérifier l'absence de fichiers .env
if git diff --cached --name-only | grep -E "\.env$"; then
echo "ERREUR : Fichier .env détecté !"
exit 1
fi
echo "Vérifications pre-commit réussies ✅"
exit 0
chmod +x .githooks/commit-msg .githooks/pre-commit
# Configurer Git pour utiliser ces hooks
git config core.hooksPath .githooks
# Tester le hook commit-msg
git add .githooks/
git commit -m "mauvais message sans format" # Devrait échouer
git commit -m "chore: ajouter les hooks Git de l'équipe" # Devrait réussir
git push
Partie 5 : git reflog — Récupérer du travail perdu
# Créer un commit à "perdre"
echo "# Travail important" > important.md
git add important.md
git commit -m "docs: ajouter documentation importante"
# "Accidentellement" perdre ce commit
git reset --hard HEAD~1
# Paniquer... puis utiliser reflog !
git reflog
# Trouver le commit perdu
HASH_PERDU=$(git reflog | grep "docs: ajouter documentation importante" | awk '{print $1}')
echo "Commit perdu retrouvé : $HASH_PERDU"
# Récupérer le commit perdu
git switch -c recuperation/$HASH_PERDU
git cherry-pick $HASH_PERDU
# Ou directement reset vers ce commit
# git reset --hard $HASH_PERDU
# Fusionner sur main
git switch main
git merge recuperation/$HASH_PERDU
git branch -d recuperation/$HASH_PERDU
# Vérifier que le fichier est récupéré
cat important.md
git push
Checklist de validation
- Les tags
v1.0.0etv1.1.0sont visibles sur GitHub avec leurs notes de release - Un cherry-pick a été effectué de main vers release/v1.0
-
git bisecta trouvé le commit qui a introduit le bug - Les hooks
.githooks/pre-commitet.githooks/commit-msgfonctionnent - Un commit "perdu" a été récupéré avec
git reflog
Résumé
Félicitations ! Vous avez maîtrisé les techniques Git avancées :
- Versionnage avec tags SemVer et releases GitHub
- Cherry-pick pour des corrections ciblées
- Bisect pour debugger l'historique
- Hooks pour automatiser la qualité du code
- Reflog comme filet de sécurité ultime
Vous êtes maintenant un utilisateur Git avancé !