git rebase

Branches et fusions

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)
[ ] facultatif| ou<arg> valeur à remplacer... répétable--flag option

Options

Cliquez sur une option pour la déplier

Débutant (3)

Reprendre le rebase après avoir résolu les conflits. À lancer après avoir indexé les fichiers résolus avec git add.

RecommandéLa façon normale de reprendre après avoir résolu et préparé les conflits

Exemple

git rebase --continue

Annuler le rebase et revenir à l’état d’origine, d’avant le début du rebase. À utiliser quand les conflits sont trop complexes ou que vous avez changé d’avis.

RecommandéPorte de sortie sans risque : remet votre branche dans son état d’avant le rebase

Exemple

git rebase --abort

Sauter le commit courant et passer au suivant. À utiliser quand un commit devient vide ou que ses modifications ne sont plus nécessaires.

Si nécessaireAbandonne le commit en cours — seulement si ses modifications sont vraiment indésirables

Exemple

git rebase --skip

Intermédiaire (3)

Ouvrir un éditeur pour réordonner, regrouper, modifier ou retirer des commits avant de les rejouer. Le moyen le plus puissant de nettoyer l’historique.

RecommandéLa meilleure façon de ranger des commits locaux avant de les partager

Exemple

git rebase -i HEAD~3

Mettre automatiquement de côté les modifications non committées avant le rebase et les restaurer après. Évite de faire un stash à la main.

Si nécessairePratique avec un arbre modifié, mais la réapplication du stash peut entrer en conflit

Exemple

git rebase --autostash main

Forcer le rebase même quand la branche est à jour. Crée de nouveaux commits avec de nouveaux hashes pour tous les commits de la plage.

Si nécessaireSeulement si vous avez besoin de nouveaux hashs pour des commits déjà à jour

Exemple

git rebase -f main

Avancé (5)

Transplanter des commits sur une autre base. Permet de rebaser les commits d’une branche sur un autre commit précis.

Si nécessaireDéplace une plage précise de commits sur une nouvelle base — vérifiez bien la plage

Exemple

git rebase --onto main feature~3 feature

Déplacer et regrouper automatiquement les commits fixup!/squash! (créés avec git commit --fixup ou --squash) dans leurs cibles. Fonctionne avec ou sans -i depuis Git 2.44.

RecommandéVa avec commit --fixup pour nettoyer l’historique sans peine

Exemple

git rebase -i --autosquash main

Préserver les commits de fusion et la structure des branches pendant le rebase. Recrée la topologie des fusions au lieu d’aplatir l’historique.

Si nécessaireQuand la structure de fusions d’une branche doit survivre au rebase

Exemple

git rebase -r main

Exécuter une commande shell après l’application de chaque commit. Utile pour lancer les tests et vérifier que chaque commit se construit.

Si nécessaireLance les tests sur chaque commit rejoué ; lent sur les longues branches

Exemple

git rebase -i -x 'npm test' main

Rebaser tous les commits accessibles depuis HEAD, y compris le commit racine. Permet de réécrire tout l’historique.

DéconseilléRéécrit tout l’historique depuis le premier commit ; rarement utile

Exemple

git rebase -i --root

Choisir la bonne option

Comparer git rebase et git merge

Savoir quand utiliser rebase ou merge pour intégrer des modifications

CommandeAspectGit RebaseGit Merge
HistoryLinéaire (réécrit les commits)Préserve l’historique divergent
CommitsCrée de NOUVEAUX commits aux SHA différentsPréserve les commits d’origine + un commit de fusion
DestructiveOui - change les hashes des commitsNon - ajoute seulement un commit de fusion
ConflictsRésolus commit par commit pendant la relectureRésolus une fois au point de fusion
Best forBranches locales/privées avant le pushBranches publiques/partagées
TraceabilityPoints d’intégration plus difficiles à voirTrace claire de l’intégration des branches

Exemples de workflows

1
git fetch origin

Récupérer les dernières modifications du dépôt distant

2
git checkout feature

Passer sur votre branche de fonctionnalité

3
git rebase origin/main

Rejouer vos commits par-dessus le dernier main

4
git push --force-with-lease

Pousser la branche rebasée (seulement si elle n’est pas partagée)

1
git log --oneline

Compter combien de commits regrouper

2
git rebase -i HEAD~5

Rebase interactif des 5 derniers commits

3
(in editor) pick, squash, squash...

Garder le premier, y regrouper les autres

4
(edit combined message)

Écrire un message de commit propre

1
(make your fix)

Corriger le problème dans le code

2
git commit --fixup=<commit-hash>

Créer un commit fixup pour la cible

3
git rebase --autosquash main

Autosquash range et regroupe automatiquement (pas besoin de -i depuis Git 2.44)

1
git rebase -i HEAD~4

Rebase interactif des 4 derniers commits

2
(in editor) reorder lines

Déplacer les lignes de commits

3
save and close editor

Git les rejoue dans le nouvel ordre

1
git rebase -i HEAD~3

Lancer un rebase interactif

2
(mark commit with 'edit')

S’arrêter sur le commit à découper

3
git reset HEAD^

Annuler le commit, garder les modifications non indexées dans le répertoire de travail

4
git add file1 && git commit -m 'Part 1'

Créer le premier petit commit

5
git add file2 && git commit -m 'Part 2'

Créer le second petit commit

6
git rebase --continue

Continuer avec les commits restants

1
git reflog

Trouver le commit d’avant le rebase

2
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 --continue

Cannot 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 main

Un 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 --skip

Le 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 --abort

Commits 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-lease

git 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 main

La 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..HEAD

Astuces 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

A:

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.

A:

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.

A:

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.

A:

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.

A:

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é.

A:

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

A:

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.

A:

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

A:

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.

A:

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.