Skip to Content
CLIGit workflows avancés

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 pull

Quand 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.1

Quand 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 abc1234

Important : 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 abc1234

Piè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 abc1234

Git 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-correction

Avantages :

  • 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 prune

Bonne pratique : nettoyer chaque worktree avec git worktree remove dè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 abc1234

Quand 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 fi

Rendre exécutable :

chmod +x .git/hooks/prepare-commit-msg

Stash 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 à committer

Stash 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 -a

Appliquer 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 drop

Cherry-pick en pratique

Complément à la section git. Quand et pourquoi l’utiliser.

Cas d’usage typiques

  1. Correction critique sur une release : un fix trouvé sur develop doit être sur release/1.2.x sans attendre la prochaine release.
git checkout release/1.2.x git cherry-pick abc1234 # le commit de fix sur develop
  1. 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 ordre

Commandes 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 --all

Récapitulatif des pièges

PiègeSymptômeSolution
Commit sur detached HEADHEAD détaché, pas de branchegit switch -c nom commit
Force push sans leaseÉcrasement silencieuxToujours --force-with-lease
Rebase sur branche partagéeConflits chez les collèguesNe jamais rebase de main/develop
Reset —hard oubliéModifications perduesgit reflog pour retrouver
Cherry-pick sur commit remaniéDifférences inattenduesgit diff HEAD commit avant cherry-pick
Stash qui écrase des modificationsPerte de travail en coursgit stash push --keep-index
Worktree danglingRéférencé mais dossier inexistantgit worktree prune