O03 Rebase & Historique propre

Un historique Git propre est un atout professionnel majeur. Ce module couvre les outils de réécriture d'historique : rebase, cherry-pick, reset et le filet de sécurité reflog.

1 · git rebase

git rebase déplace les commits d'une branche en les "rejouant" sur un nouveau commit de base. Contrairement au merge, il produit un historique linéaire sans commit de fusion parasite.

Merge vs Rebase

main: A─B─C \ feature: D─E─F (merge commit) main: A─B─C────M \──────/

Avec merge — historique non linéaire

main: A─B─C \ feature: D─E─F (après rebase) main: A─B─C─D'─E'─F'

Avec rebase — historique linéaire

Commande de base

# Se placer sur la branche feature
git switch feature/login

# Rejouer les commits de feature sur main
git rebase main

# Ensuite fast-forward merge sur main
git switch main
git merge --ff-only feature/login

Résoudre les conflits pendant un rebase

# Git s'arrête sur chaque commit conflictuel
# 1. Résoudre manuellement les fichiers
git add fichier-resolu.txt

# 2. Continuer le rebase
git rebase --continue

# Ou annuler et revenir à l'état initial
git rebase --abort
Bonne pratique : Ne jamais rebaser une branche déjà poussée sur origin et partagée avec d'autres. Rebase est pour les branches locales non encore partagées.

2 · Rebase interactif

Le rebase interactif (-i) ouvre un éditeur listant les commits à rejouer. On peut réordonner, fusionner, modifier, ou supprimer des commits.

Lancer un rebase interactif

# Modifier les 3 derniers commits
git rebase -i HEAD~3

# Modifier depuis un commit précis
git rebase -i abc1234^

Liste des commandes disponibles

  • pick p — Garder le commit tel quel (action par défaut)
  • reword r — Garder le commit mais modifier son message
  • edit e — Arrêter pour modifier le contenu du commit
  • squash s — Fusionner avec le commit précédent, concaténer les messages
  • fixup f — Fusionner avec le précédent, ignorer ce message
  • drop d — Supprimer le commit entièrement

Exemple : squasher 3 commits en 1

# Fichier ouvert par git rebase -i HEAD~3
pick a1b2c3 feat: ajout formulaire login
s   d4e5f6 fix: correction validation email
s   g7h8i9 style: mise en forme bouton submit

# → Git ouvrira un second éditeur pour le message final combiné
# Résultat : 1 seul commit "feat: ajout formulaire login complet"

Exemple : reword pour corriger un message

# Fichier de rebase interactif
pick a1b2c3 feat: add login form
r    d4e5f6 fix typo  ← "r" au lieu de "pick"
pick g7h8i9 docs: mise à jour README

# Git va s'arrêter sur d4e5f6 pour vous laisser changer le message
⚠ Attention : Un rebase interactif réécrit les SHA des commits. Si vous avez déjà poussé ces commits, un force-push sera nécessaire — ce qui impacte les collaborateurs.

3 · git cherry-pick

git cherry-pick applique un commit spécifique (identifié par son hash) sur la branche courante, sans fusionner toute la branche source. Idéal pour backporter un correctif.

Appliquer un commit précis

# Récupérer le hash du commit à appliquer
git log --oneline feature/login
# → abc1234 fix: correction faille XSS dans le formulaire

# Appliquer ce seul commit sur main
git switch main
git cherry-pick abc1234

Cherry-pick d'une plage de commits

# Appliquer les commits de abc1234 jusqu'à def5678 (inclus)
git cherry-pick abc1234^..def5678

# Options utiles
git cherry-pick -n abc1234   # --no-commit : stager sans committer
git cherry-pick -x abc1234   # Ajouter la référence source dans le message

Cas d'usage typique : hotfix backport

# Un correctif urgent a été fait sur develop
# On doit l'appliquer sur la branche release/v2.0 sans merger develop entier

git switch release/v2.0
git cherry-pick f1a2b3c    # le hash du correctif
git push origin release/v2.0
Astuce : Utilisez git log --all --oneline --graph pour visualiser les commits de toutes les branches et retrouver le hash exact à cherry-picker.

4 · git reset

git reset déplace le pointeur HEAD (et la branche courante) vers un commit antérieur. Le comportement sur les fichiers dépend du mode utilisé.

Les 3 modes de reset

Mode Commande Index (staging) Working directory
--soft git reset --soft HEAD~1 Inchangé (fichiers stagés) Inchangé
--mixed git reset HEAD~1 (défaut) Vidé (unstaged) Inchangé
--hard git reset --hard HEAD~1 Vidé Effacé ⚠

Exemples pratiques

# --soft : défaire le dernier commit mais garder les changements stagés
git reset --soft HEAD~1
# → Les fichiers restent dans "Changes to be committed"

# --mixed : défaire le commit ET le staging (défaut)
git reset HEAD~1
# → Les fichiers restent modifiés mais non stagés

# --hard : TOUT effacer, revenir exactement à HEAD~1
git reset --hard HEAD~1
# ⚠ Danger : les modifications non commitées sont PERDUES

Reset vers un commit précis

# Revenir à un commit spécifique
git reset --hard abc1234

# Défaire le staging d'un seul fichier
git reset HEAD fichier.txt
# équivalent moderne :
git restore --staged fichier.txt
⚠ --hard est destructif : Les fichiers modifiés non commités sont perdus définitivement (sauf via reflog dans un délai limité). Préférez --soft ou --mixed dans le doute.

5 · git reflog

git reflog est le filet de sécurité ultime de Git. Il enregistre tous les déplacements de HEAD, même ceux qui "effacent" des commits. Un commit "perdu" peut être retrouvé via le reflog pendant ~90 jours.

Lire le reflog

git reflog

# Sortie typique :
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
f4e5d6c HEAD@{1}: commit: feat: ajout page contact
b7c8d9e HEAD@{2}: commit: fix: correction bug #42
0a1b2c3 HEAD@{3}: checkout: moving from main to feature/contact

Récupérer des commits après un reset --hard

# Scénario : git reset --hard HEAD~3 effectué par erreur
git reflog
# → f4e5d6c HEAD@{1}: commit: feat: fonctionnalité importante

# Méthode 1 : revenir directement via le reflog
git reset --hard HEAD@{1}

# Méthode 2 : créer une nouvelle branche depuis l'état récupéré
git checkout -b recuperation HEAD@{1}

# Méthode 3 : via le hash direct
git checkout f4e5d6c

Reflog d'une branche spécifique

# Voir l'historique complet d'une branche
git reflog refs/heads/main
git reflog origin/main

# Afficher avec dates
git reflog --date=iso
Durée de rétention : Par défaut Git conserve le reflog 90 jours (gc.reflogExpire). Passé ce délai, les commits orphelins sont nettoyés par git gc.

6 · Réécriture d'historique

Git offre plusieurs façons de réécrire l'historique. Ces opérations sont puissantes mais doivent être utilisées avec précaution, surtout sur des branches partagées.

git commit --amend

# Modifier le message du dernier commit
git commit --amend -m "feat: nouveau message corrigé"

# Ajouter des fichiers oubliés sans changer le message
git add fichier-oublié.js
git commit --amend --no-edit

# Modifier auteur
git commit --amend --author="Alaa EL HUSSEIN <alaa@example.com>"

Force-push après réécriture

# ⚠ Danger : écrase l'historique distant
git push --force origin feature/login

# ✅ Mieux : --force-with-lease vérifie qu'on n'écrase pas le travail d'un autre
git push --force-with-lease origin feature/login

La Règle d'Or de Git

Ne jamais réécrire l'historique de commits déjà poussés et partagés avec d'autres. Un rebase, amend ou reset sur des commits publics crée des divergences pour tous les collaborateurs qui ont basé leur travail dessus. Réservez ces opérations aux branches locales ou aux branches de feature personnelles pas encore mergées.

Tableau récapitulatif : quel outil utiliser ?

BesoinOutil recommandéRisque
Corriger le dernier messagegit commit --amendFaible (si pas encore pushé)
Fusionner plusieurs commitsgit rebase -i squashMoyen
Linéariser l'historiquegit rebase mainMoyen
Défaire un commit (garder code)git reset --softFaible
Tout effacergit reset --hardÉlevé ⚠
Récupérer après erreurgit reflogNul (lecture seule)
← O02 : Branches & Merge
Exercices O03 → O04 : GitHub Remote →