git rebase
Réappliquer des commits par-dessus une autre base
git rebase déplace ou regroupe une suite de commits sur un nouveau commit de base. On s’en sert souvent pour garder un historique linéaire en rejouant les commits d’une branche de fonctionnalité par-dessus le dernier main. Contrairement à merge, rebase réécrit l’historique en créant de nouveaux commits aux SHA différents. Le rebase interactif (-i) permet de regrouper, réordonner, modifier ou retirer des commits avant de les rejouer.
Synopsis
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)Options
Choisir la bonne option
Comparer git rebase et git merge
Savoir quand utiliser rebase ou merge pour intégrer des modifications
| Commande | Aspect | Git Rebase | Git Merge |
|---|---|---|---|
History | Linéaire (réécrit les commits) | Préserve l’historique divergent | |
Commits | Crée de NOUVEAUX commits aux SHA différents | Préserve les commits d’origine + un commit de fusion | |
Destructive | Oui - change les hashes des commits | Non - ajoute seulement un commit de fusion | |
Conflicts | Résolus commit par commit pendant la relecture | Résolus une fois au point de fusion | |
Best for | Branches locales/privées avant le push | Branches publiques/partagées | |
Traceability | Points d’intégration plus difficiles à voir | Trace claire de l’intégration des branches |
Exemples de workflows
git fetch originRécupérer les dernières modifications du dépôt distant
git checkout featurePasser sur votre branche de fonctionnalité
git rebase origin/mainRejouer vos commits par-dessus le dernier main
git push --force-with-leasePousser la branche rebasée (seulement si elle n’est pas partagée)
git log --onelineCompter combien de commits regrouper
git rebase -i HEAD~5Rebase interactif des 5 derniers commits
(in editor) pick, squash, squash...Garder le premier, y regrouper les autres
(edit combined message)Écrire un message de commit propre
(make your fix)Corriger le problème dans le code
git commit --fixup=<commit-hash>Créer un commit fixup pour la cible
git rebase --autosquash mainAutosquash range et regroupe automatiquement (pas besoin de -i depuis Git 2.44)
git rebase -i HEAD~4Rebase interactif des 4 derniers commits
(in editor) reorder linesDéplacer les lignes de commits
save and close editorGit les rejoue dans le nouvel ordre
git rebase -i HEAD~3Lancer un rebase interactif
(mark commit with 'edit')S’arrêter sur le commit à découper
git reset HEAD^Annuler le commit, garder les modifications non indexées dans le répertoire de travail
git add file1 && git commit -m 'Part 1'Créer le premier petit commit
git add file2 && git commit -m 'Part 2'Créer le second petit commit
git rebase --continueContinuer avec les commits restants
git reflogTrouver le commit d’avant le rebase
git reset --hard HEAD@{n}Revenir à l’état d’avant le rebase
Dépannage
CONFLIT pendant un rebase
Cause : Les modifications de votre commit entrent en conflit avec la branche de base
Modifiez les fichiers pour résoudre les conflits, faites git add, puis git rebase --continue
git add <resolved-files> && git rebase --continueCannot rebase: You have unstaged changes
Cause : Le répertoire de travail contient des modifications non committées
Committez, faites un stash, ou utilisez --autostash pour gérer les modifications non committées
git rebase --autostash mainUn commit devient vide après le rebase
Cause : Les modifications de ce commit existent déjà dans la branche cible
Un rebase simple le retire automatiquement ; un rebase interactif s’arrête - lancez --skip pour le retirer, ou 'git commit --allow-empty' pour le garder
git rebase --skipLe rebase provoque de nombreux conflits
Cause : La branche a beaucoup divergé de la base
Envisagez plutôt un merge, ou abandonnez et rebasez par petites étapes
git rebase --abortCommits perdus après un rebase
Cause : Les commits d’origine sont orphelins (pas supprimés, juste plus référencés)
Utilisez le reflog pour retrouver et récupérer les commits d’origine
git reflog && git branch recovery HEAD@{n}Push rejeté (non-fast-forward) après un rebase
Cause : Le rebase a réécrit les commits, donc votre branche ne contient plus l’historique de la copie distante
Utilisez --force-with-lease pour un force push plus sûr (il refuse si quelqu’un d’autre a poussé entre-temps)
git push --force-with-leasegit rebase dit « Current branch is up to date »
Cause : Aucun commit à rejouer - la branche est déjà basée sur la cible
Utilisez -f pour forcer le rebase malgré tout si vous avez besoin de nouveaux hashes
git rebase -f mainLa liste de tâches du rebase interactif est vide
Cause : Aucun commit dans la plage indiquée
Vérifiez votre plage - utilisez HEAD~n ou indiquez le bon upstream
git log --oneline main..HEADAstuces et pièges
Bonnes pratiques
- ✓Ne rebasez jamais des commits poussés sur un dépôt partagé - cela oblige tout le monde à réparer ses branches à la main
- ✓Utilisez toujours 'git rebase --abort' si les conflits deviennent trop complexes - vous pourrez essayer une autre approche
- ✓Utilisez le rebase interactif (-i) pour nettoyer les commits avant de pousser ou de créer une pull request
- ✓Pensez à '--autostash' pour gérer automatiquement les modifications non committées pendant le rebase
- ✓Utilisez 'git commit --fixup=<hash>' avec '--autosquash' pour un workflow de correction plus propre
- ✓Lancez les tests après un rebase pour vérifier que les commits rebasés fonctionnent toujours
- ✓Utilisez '-x npm test' pour lancer automatiquement les tests après chaque commit rejoué
- ✓Gardez les rebases petits - rebasez souvent plutôt que de laisser les branches diverger fortement
- ✓Utilisez 'git reflog' pour vous rattraper si un rebase tourne mal - les commits d’origine ne sont jamais supprimés immédiatement
- ✓Préférez merge à rebase pour les branches publiques/partagées afin de préserver l’historique
- ✓Utilisez '--force-with-lease' plutôt que '--force' pour pousser des branches rebasées, par sécurité
- ✓Quand vous regroupez des commits, écrivez un nouveau message descriptif qui résume tous les commits regroupés
Erreurs fréquentes
- ✗Rebaser des commits déjà poussés sur un dépôt partagé. Cela réécrit l’historique et oblige tous les autres à réparer leurs branches à la main.
- ✗Oublier d’indexer les conflits résolus avant --continue. Faites toujours 'git add' sur les fichiers résolus d’abord.
- ✗Utiliser --skip à la légère, ce qui retire les modifications du commit de la branche rebasée. À réserver au retrait volontaire d’un commit.
- ✗Ne pas comprendre que rebase crée de NOUVEAUX commits avec des hashes différents. Les commits d’origine deviennent orphelins.
- ✗Rebaser avec des modifications non committées dans le répertoire de travail. Utilisez --autostash ou faites d’abord un stash.
FAQ
Merge préserve l’historique en créant un commit de fusion, qui garde intacts les historiques des deux branches. Rebase réécrit l’historique en rejouant vos commits par-dessus une autre branche, ce qui donne un historique linéaire. Utilisez merge pour les branches partagées, rebase pour le nettoyage local.
Rebaser change les hashes des commits. Si d’autres ont basé leur travail sur vos commits d’origine, leur historique divergera du vôtre. Ils devront réparer leurs branches à la main, ce qui est fastidieux et source d’erreurs.
Utilisez 'git reflog' pour trouver le commit d’avant le rebase (cherchez l’ancienne pointe de la branche), puis 'git reset --hard HEAD@{n}' pour le restaurer. Par défaut, les commits d’origine restent accessibles depuis le reflog pendant 30 jours. Juste après un rebase, 'git reset --hard ORIG_HEAD' fonctionne aussi.
Dans un rebase interactif : 'pick' garde le commit tel quel, 'squash' le regroupe avec le commit précédent (en gardant les deux messages), 'fixup' le regroupe mais jette son message, 'reword' garde le commit mais permet de modifier son message.
Lancez 'git rebase -i HEAD~n', où n est le nombre de commits. Dans l’éditeur, gardez 'pick' sur le premier commit et remplacez les autres par 'squash' ou 'fixup'. Enregistrez, puis modifiez le message du commit combiné.
--onto permet de transplanter des commits sur une autre base. Par exemple, « git rebase --onto main feature~3 feature » ne déplace que les 3 derniers commits de feature sur main, en laissant de côté les plus anciens.
Modifiez les fichiers en conflit pour les résoudre, faites 'git add' sur les fichiers résolus, puis 'git rebase --continue'. Si c’est trop compliqué, 'git rebase --abort' vous ramène à l’état d’avant le rebase.
--preserve-merges a été supprimé dans Git 2.34 (git échoue désormais avec « --preserve-merges was replaced by --rebase-merges »). Utilisez plutôt --rebase-merges (-r) pour préserver les commits de fusion et la structure des branches pendant le rebase. Il recrée la topologie des fusions sur la nouvelle base.
Oui, utilisez 'git rebase -i' et marquez les commits à garder avec 'pick' et ceux à retirer avec 'drop'. Ou utilisez --onto pour préciser exactement la plage de commits à déplacer.
Le hash d’un commit est calculé à partir de son contenu, de son message, de son auteur, de sa date ET de son commit parent. Comme rebase change le parent, tous les commits suivants reçoivent de nouveaux hashes, même si leur contenu est identique.