Git — Workflows avancés
Stratégies de branching, récupération, et bonnes pratiques pour les développeurs qui utilisent Git au quotidien.
Cette page complète la référence de commandes Git (git) en se concentrant sur les décisions de workflow et les pièges courants.
Stratégies de branching
Le choix de stratégie impacte la productivité de l’équipe et la complexité du workflow.
Trunk-based development
Approche minimaliste : une seule branche principale (main), des features livrées via des branches de courte durée (1-3 jours).
# Créer une feature courte
git switch -c feat/ma-fonctionnalité main
# Travailler, commiter souvent
# Pousser chaque soir ou en fin de journée
# Ouvrir une PR depuis la branche feature
# Après review et merge, suppression de la branche
git push origin feat/ma-fonctionnalitéQuand utiliser : petites équipes, CI/CD continue, déploiements fréquents.
Avantages : simplicité, pas de merge commits lourds, intégration continue.
Inconvénients : nécessite des features découpables et un CI robuste.
GitHub Flow
Similaire au trunk-based mais avec un PR obligatoire pour chaque changement sur main.
# Workflow typique
git switch -c feat/mon-changement main
# ... développement ...
git push origin feat/mon-changement
# → Ouvrir un Pull Request sur GitHub/GitLab
# → Review, CI passe, merge
git switch main && git pullQuand utiliser : déploiement continu, équipes mid-size, pas de versions figées.
Gitflow
Structure rigide avec branches develop, release/*, hotfix/*, et feature/*.
main ─────────────── main (tagué)
│ ▲
│ release/* │ hotfix/*
│ develop │ develop
│ ▲ │ ▲
│ │ feature/* │ │ feature/*
│ └────────────────┘ └─────────────────# Initialisation
git checkout -b develop origin/main
# Feature
git checkout -b feature/nom develop
# ... travail ...
git checkout develop
git merge --no-ff feature/nom
# Release
git checkout -b release/1.2.0 develop
# corrections mineures uniquement
git checkout main
git merge --no-ff release/1.2.0
git tag -a v1.2.0
git checkout develop
git merge --no-ff release/1.2.0
# Hotfix
git checkout -b hotfix/1.2.1 main
# correction critique
git checkout main
git merge --no-ff hotfix/1.2.1
git tag -a v1.2.1
git checkout develop
git merge --no-ff hotfix/1.2.1Quand utiliser : releases planifiées, cycles de version longs, équipes grandes.
Piège : la complexité. Pour la majorité des projets, trunk-based + GitHub Flow suffit.
Reflog — Récupérer ce qui semble perdu
git reflog catalogue chaque déplacement de HEAD. C’est un outil de récupération qui permet de retrouver des commits “perdus”.
# Voir l'historique des mouvements de HEAD
git reflog
# Retrouver un commit "perdu" après un reset --hard
git reflog
# Exemple de sortie :
# abc1234 HEAD@{2}: reset: moving to HEAD~1
# def5678 HEAD@{3}: commit: fix bug critique
# Restaurer
git reset --hard def5678# Cas : on a supprimé une branche
git branch -D ma-feature # erreur de manipulation
git reflog | grep ma-feature # trouver le dernier commit
git branch ma-feature abc1234 # recréer la branche# Cas : checkout d'un ancien commit (detached HEAD récupérable)
git reflog # identifier le commit voulu
git switch -c sauvegarde abc1234Important : le reflog ne supprime pas les objets Git immédiatement. Ils sont conservés au moins 90 jours par défaut avant le garbage collection (
git gc --prune=now).
Detached HEAD — Comprendre et récupérer
Quand git checkout (ou git switch) pointe vers un commit spécifique et non vers une branche, Git est en mode “detached HEAD”.
# Entrer en detached HEAD
git switch abc1234 # ancien commit
# HEAD détaché, à abc1234
# Explorer l'historique d'un commit spécifique
git switch --detach HEAD~5
# Créer une branche depuis ce commit (pas recommandé)
git switch -c temp abc1234Piège : si vous committez en detached HEAD sans créer de branche, ces commits ne sont accessibles que via git reflog.
Bonne pratique : créer systématiquement une branche quand on travaille sur un commit ancien.
# Sécurisé : créer une branche avant de vérifier un commit
git switch -c debug-old-commits abc1234Git worktree — Travailler sur plusieurs branches simultanément
git worktree crée un second workspace pointant vers la même archive Git, pour chaque branche simultanément, sans avoir à stasher ou switcher. Idéal pour passer rapidement d’une feature à un hotfix sans perdre le contexte.
# Créer un nouveau worktree à partir de main
git worktree add ../mon-projet-correction -b fix/correction-critique
# Lister tous les worktrees actifs
git worktree list
# Résultat attendu :
# /home/user/mon-projet abc1234 HEAD -> main
# /home/user/mon-projet-correction def5678 HEAD -> fix/correction-critique
# Travailler dans le nouveau dossier
cd ../mon-projet-correction
# ... développement du hotfix ...
git commit -m "fix: corriger le bug critique"
git push origin fix/correction-critique
# Nettoyage : supprimer le worktree quand la branche est mergée
git worktree remove ../mon-projet-correctionAvantages :
- Pas de stash, pas de perte de contexte entre les branches.
- Possibilité de lancer deux serveurs simultanément (une version avec la feature, une autre propre).
- Le worktree partage le même repo
.git, donc il n’y a pas de duplication de l’historique.
Piège : si le worktree n’est pas nettoyé, il garde une référence vers la branche. Supprimer un worktree dangling :
# Nettoyer les worktrees orphelins (dossier supprimé manuellement)
git worktree pruneBonne pratique : nettoyer chaque worktree avec
git worktree removedès que la branche est mergée ou abandonnée, pour ne pas laisser de références orphelines.
Force push sécurisé
Le force push réécrit l’historique distant. Paradoxalement, --force-with-lease est plus sûr que --force.
# ❌ DANGEREUX : écrase ce que les autres ont poussé
git push --force origin ma-feature
# ✅ SÛR : refuse si quelqu'un d'autre a poussé entre-temps
git push --force-with-lease origin ma-feature
# Vérifier avant un force push nécessaire
git fetch origin
git log --oneline origin/ma-feature ^ma-feature
# Si des commits apparaissent ici, les récupérer d'abord
git cherry-pick abc1234Quand forcer est justifié : après un rebase interactif local, sur une branche personnelle non partagée.
Quand forcer est interdit : sur main, develop, ou toute branche collaborée.
Bonnes pratiques de commit
Message de commit structuré
type(scope): description courte
Description longue si nécessaire.
Motivations, contexte, références de tickets.Types courants : feat, fix, docs, style, refactor, test, chore.
Commits atomiques
Un commit = une modification logique unique. Éviter les commits “je fais tout ce que j’ai fait aujourd’hui”.
.git/hooks/prepare-commit-msg
Personnaliser automatiquement le message de commit avec l’ID du ticket :
# .git/hooks/prepare-commit-msg
#!/bin/sh
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
# Ajouter automatiquement le numéro de branche dans le message
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
if [ -n "$BRANCH" ] && [ "$COMMIT_SOURCE" != "merge" ]; then
TICKET=$(echo "$BRANCH" | grep -oP '(?<=/)[A-Z]+-[0-9]+')
if [ -n "$TICKET" ]; then
MSG=$(cat "$COMMIT_MSG_FILE")
echo "$MSG" | head -1 | grep -q "$TICKET" || echo "" >> "$COMMIT_MSG_FILE"
echo "Réf: $TICKET" >> "$COMMIT_MSG_FILE"
fi
fiRendre exécutable :
chmod +x .git/hooks/prepare-commit-msgStash avancé
Le stash de base est couvert dans git. Voici les patterns avancés.
Garder les modifications staged après un stash
# Stasher uniquement les modifications non-stagées
# Les fichiers déjà en stage restent dans l'index
git stash push --keep-index
# Utile quand on veut stasher le travail "en vrac"
# sans perdre les fichiers prêts à committerStash avec des changements spécifiques
# Stasher en ne prenant que certains fichiers
git stash push -- fichier1.txt fichier2.txt
# Stasher tous les changements, fichiers trackés + non-trackés + ignorés
git stash push -aAppliquer un stash avec résolution de conflits
# Si le stash crée des conflits avec la branche actuelle
git stash apply # échoue silencieusement, les conflits sont dans la workspace
# Voir ce qui a conflit
git diff --name-only --diff-filter=U
# Résoudre, puis valider le stash
git add fichiers_resolus
git stash dropCherry-pick en pratique
Complément à la section git. Quand et pourquoi l’utiliser.
Cas d’usage typiques
- Correction critique sur une release : un fix trouvé sur
developdoit être surrelease/1.2.xsans attendre la prochaine release.
git checkout release/1.2.x
git cherry-pick abc1234 # le commit de fix sur develop- Séparer un gros commit : un commit fait deux choses, on veut un commit propre par fonctionnalité.
git reset HEAD~1 # annuler le commit (chaud dans le working dir)
git add -p fichier.txt # sélectionner les hunks du fix
git commit -m "fix: corriger le bug X"
git add -p fichier.txt # sélectionner les hunks de la feature
git commit -m "feat: ajouter la fonctionnalité Y"Pièges du cherry-pick
# ❌ Le merge-base change : le cherry-pick inclut des changements inattendus
git cherry-pick abc1234 # abc1234 a été modifié depuis (rebased)
# ✅ Vérifier avant d'appliquer
git show abc1234 --stat
git diff HEAD abc1234 # voir exactement ce qui va être appliqué
# Multi-commit cherry-pick : attention à l'ordre
git cherry-pick abc1234 def5678 ghi9012 # dans cet ordreCommandes de débogage Git
# Comprendre pourquoi un fichier a changé
git diff HEAD~3 -- fichier.txt
# Trouver quand une ligne a été ajoutée
git log -L :ma_fonction:fichier.py
# Analyser le graphe des branches locales
git log --oneline --graph --all --decorate -20
# Vérifier l'intégrité du repo
git fsck --full
# Statistiques de contribution
git shortlog -sn --allRécapitulatif des pièges
| Piège | Symptôme | Solution |
|---|---|---|
| Commit sur detached HEAD | HEAD détaché, pas de branche | git switch -c nom commit |
| Force push sans lease | Écrasement silencieux | Toujours --force-with-lease |
| Rebase sur branche partagée | Conflits chez les collègues | Ne jamais rebase de main/develop |
| Reset —hard oublié | Modifications perdues | git reflog pour retrouver |
| Cherry-pick sur commit remanié | Différences inattendues | git diff HEAD commit avant cherry-pick |
| Stash qui écrase des modifications | Perte de travail en cours | git stash push --keep-index |
| Worktree dangling | Référencé mais dossier inexistant | git worktree prune |