Rebase interactif — Réécrire l'histoire
Le rebase interactif (git rebase -i) est l'outil le plus puissant pour nettoyer un historique avant de pousser. Il vous permet de modifier, fusionner, réordonner et supprimer des commits comme si vous réécriviez le scénario d'un film.
Les actions disponibles
Quand vous lancez git rebase -i, Git ouvre un éditeur avec une liste de commits et des actions :
pick a1b2c3 feat(nav): add responsive navigation
pick b2c3d4 fix typo in Header.astro
pick c3d4e5 feat(nav): add drawer for mobile
pick d4e5f6 WIP: broken, dont merge
pick e5f6a7 fix: actually make drawer close
| Commande | Alias | Effet |
|---|---|---|
pick | p | Garder le commit tel quel |
reword | r | Garder le commit, modifier le message |
edit | e | S'arrêter pour modifier le contenu du commit |
squash | s | Fusionner avec le commit précédent, combiner les messages |
fixup | f | Fusionner avec le commit précédent, garder seulement le message précédent |
drop | d | Supprimer le commit |
| (réordonner les lignes) | Réordonner les commits |
TP — Nettoyer l'historique de feature/responsive-nav
Situation de départ
La branche feature/responsive-nav ressemble à ça dans la vraie vie :
cd ~/git-workshop/ng-baguette-conf
git checkout feature/responsive-nav
# Simuler un historique de feature "honnête"
git log --oneline feature/responsive-nav ^main
# e.g.:
# a1b2c3 feat(nav): replace dropdown with drawer for mobile navigation
Ajoutons l'historique réaliste d'une vraie session de travail :
# La suite du travail en cours (commitée rapidement)
cat >> src/components/Drawer.astro << 'EOF'
<script>
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const cb = document.getElementById('mobile-nav') as HTMLInputElement | null;
if (cb) cb.checked = false;
}
});
</script>
EOF
git add . && git commit -m "close on ESC"
# Oops, on oublie de gérer le focus
cat >> src/components/Header.astro << 'EOF'
<!-- focus trap à ajouter -->
EOF
git add . && git commit -m "add focus trap comment"
# Fix d'un bug découvert en testant
sed -i '' 's/<!-- focus trap à ajouter -->//' src/components/Header.astro 2>/dev/null || \
sed -i 's/<!-- focus trap à ajouter -->//' src/components/Header.astro
git add . && git commit -m "fix bug"
# Encore un oubli
echo "" >> src/components/Drawer.astro
git add . && git commit -m "wip"
# Finalisation
git commit --allow-empty -m "done I think"
L'historique est maintenant honteux :
git log --oneline feature/responsive-nav ^main
# 5f6a7b8 done I think
# 4e5f6a7 wip
# 3d4e5f6 fix bug
# 2c3d4e5 add focus trap comment
# 1b2c3d4 close on ESC
# a9b0c1d feat(nav): replace dropdown with drawer for mobile navigation
Nettoyer avec rebase -i
# Ouvrir le rebase interactif sur tous les commits depuis main
git rebase -i main
L'éditeur s'ouvre avec :
pick a9b0c1d feat(nav): replace dropdown with drawer for mobile navigation
pick 1b2c3d4 close on ESC
pick 2c3d4e5 add focus trap comment
pick 3d4e5f6 fix bug
pick 4e5f6a7 wip
pick 5f6a7b8 done I think
Modifiez-le pour obtenir :
reword a9b0c1d feat(nav): replace dropdown with drawer for mobile navigation
fixup 1b2c3d4 close on ESC
drop 2c3d4e5 add focus trap comment
fixup 3d4e5f6 fix bug
fixup 4e5f6a7 wip
fixup 5f6a7b8 done I think
Sauvegardez et quittez. Git vous demandera de reword le premier commit :
feat(nav): replace dropdown with drawer for mobile navigation
Corrigez en :
feat(nav): replace dropdown with accessible drawer (mobile)
Résultat final
git log --oneline feature/responsive-nav ^main
# a9b0c1d feat(nav): replace dropdown with accessible drawer (mobile)
Six commits douteux → un seul commit propre qui raconte une histoire claire.
TP — Squash de commits WIP
Scénario fréquent : vous avez commité en cours de route avec des messages wip, fix, oups.
# Voir les 5 derniers commits sur main
git log --oneline -5
Fusionner les 3 derniers commits en un seul :
git rebase -i HEAD~3
Mettez tous sauf le premier en fixup :
pick abc123 feat: add social links (Bluesky, Twitter, LinkedIn)
fixup def456 fix: correct LinkedIn URL
fixup ghi789 chore: release v1.0.0
fixup vs squashfixup: garde le message du commit au-dessus, jette le restesquash: vous demande de combiner les messages → ouvre l'éditeur
TP — Supprimer un commit
Vous avez commité des credentials ou du code qu'il ne fallait pas :
# Voir l'historique
git log --oneline -5
# Supprimer le commit coupable avec drop
git rebase -i HEAD~N
# Changez "pick" en "drop" sur la ligne concernée
Si le commit à supprimer modifie des fichiers que des commits suivants utilisent, drop créera des conflits. Résolvez-les normalement avec git rebase --continue.
TP — Réordonner des commits
Parfois vous voulez changer l'ordre pour grouper des changements logiquement :
git rebase -i HEAD~4
# Avant :
pick a feat: add Speaker component
pick b feat: add CFP page
pick c fix: fix Speaker avatar size
pick d test: add schedule tests
# Après (réordonner + squash) :
pick a feat: add Speaker component
fixup c fix: fix Speaker avatar size
pick b feat: add CFP page
drop d test: add schedule tests
--autosquash : le raccourci magique
Si vous commitez avec le préfixe fixup! ou squash! suivi du message de la cible, Git prépare le rebase automatiquement :
# Commit normal
git commit -m "feat: add Agenda component"
# Plus tard, fix à fusionner avec ce commit
git commit -m "fixup! feat: add Agenda component"
# Rebase avec autosquash
git rebase -i --autosquash main
# Git place automatiquement le fixup au bon endroit !
Combiner avec un alias :
git config --global alias.fixup 'commit --fixup'
git fixup HEAD~2 # crée un commit fixup! pour HEAD~2
git commit --amend — cas simple
Pour modifier uniquement le dernier commit (avant de pusher) :
# Modifier le message du dernier commit
git commit --amend -m "feat(nav): correct message"
# Ajouter un fichier oublié au dernier commit
git add fichier-oublie.astro
git commit --amend --no-edit # garde le même message
--amend réécrit le commit (nouveau SHA). Ne jamais amender un commit déjà pushé sur une branche partagée.