La commande git rebase fait parti des plus puissantes de Git1 : elle permet de replacer (ou re-baser pour être plus précis) une branche sur une autre.

Cette section ne va pas vous expliquer comment ça marche (pour ça je vous laisse 2 liens ici et ), mais plutôt pour vous donner des petits tuyaux.

Rebase interactif

L’option --interactive (ou -i pour les intimes) demande à Git de vous afficher les commits qui vont être repris. Vous pouvez ainsi voir ce qui va se passer (très utile — surtout quand vous pensez que non) voire modifier le comportement.

Git va lancer votre éditeur favori (sinon voyez la configuration) pour vous présenter toutes les modifications qu’il compte faire.

Note: pour annuler le rebase, lorsque l’éditeur est lancé, effacez le fichier, sauvegardez puis quitter. Pour plus d’infos, voir la sections sur Git et les éditeurs.

Pour plus d’infos, n’oubliez pas la section dédiée du git book ni git help rebase.

Comme toujours, une explication vous est fournie directement dans le fichier ouvert, dans les commentaires de fin.

L’idée est que vous verrez une ligne par commit, chaque ligne commençant par « pick ». Vous pouvez inverser l’ordre des lignes, voire en supprimer : cela aura pour effet de demander à Git d’inverser l’ordre des commits et d’en supprimer.

Vous pouvez aussi modifier le « pick ». Voici les différentes valeurs possibles (à l’heure où j’écris ces lignes) :

pick
reprend le commit tel quel
reword
reprend le commit et permet d’éditer le message
edit
reprend le commit et permet de l’éditer
squash
fusionne le commit avec le précédent, et permet d’éditer le message
fixup
pareil que squash, mais garde le message du premier commit
exec
exécute une commande

Les 3 premiers sont assez simples à comprendre : ils permettent de reprendre les commits et d’y apporter quelques modifications.

Les 2 suivants (squash et fixup) sont extrêmement intéressants, je vais les détailler dans une prochaine section.

Notes

  • Il est donc possible de modifier (ou changer l’ordre) des commits anciens, mais cela ne se fait pas forcément sans douleurs : chaque modification d’un commit risque d’entraîner des conflits dans les suivants.

  • Ne vous sentez pas obliger d’avoir un historique parfait. Avoir un commit qui corrige un autre de la même branche n’est pas grave en soit. N’oubliez pas que plus vous jouez avec l’historique, plus vous la dénaturez, et plus vous avez de chances de casser des choses… Et casser votre historiques est une des pires choses qui puisse vous arriver : si vous supprimez une modification de l’historique, vous ne pourrez plus vous y référer plus tard (on ne peut pas revert ce genre de modification)

Le git rebase est donc une commande qui — à cause de sa grande puissance — doit être utilisée avec parcimonie.

Une manière de se protéger de ce genre de mauvaises manipulation est de sauvegardez votre état actuel (en créant une nouvelle branche) puis — après avoir effectuer vos changement — de comparer votre nouvel état à celui sauvegarder. Vous pourrez ainsi vous assurer de ne pas avoir perdu de modifications en cours de routes.

Autosquash

Prenons un exemple :

Vous êtes en train de travailler sur une fonctionnalité un peu grosse, donc vous êtes dans votre propre branche (car c’est bien évidemment un réflexe chez vous, n’est-ce pas ?) et avez déjà créé quelques commits. Soudain, vous découvrez un problème dû à l’un des premiers commits de cette branche (ne serait-ce qu’un simple commentaire non mis à jour avec le code).

Vous avez plusieurs options : le laisser tel quel (ok c’est pas la meilleure), corriger le problème dans un nouveau commit (ça peut faire sens, surtout si la correction est grosse ou un peu complexe), ou modifier le commit en question pour y inclure la correction.

Nous allons nous intéressé à ce dernier cas, et aux différentes méthodes qui permettent de réaliser la modification du commit.

Soit <ref> la référence du commit à corriger.

Méthode 1 — édition manuelle d’un commit

  1. Lancez un rebase interactif
    • « git rebase --interactive <ref>^ »
  2. Demandez d’éditer le commit concerné
    • remplacez pick par edit

Cette méthode est simple à réaliser mais si vous avez des problèmes (conflits) et préférez annuler le rebase, alors vous perdez aussi la correction.

Méthode 2 — fixup

  1. Enregistrez la correction dans un nouveau commit
    • « git commit »
  2. Lancer un rebase interactif
    • « git rebase --interactive <ref>^ »
  3. Déplacez le commit de correction pour le placer juste en dessous du commit concerné
  4. Demander de fusionner le commit de correction avec le précédent
    • remplacez pick par fixup

Cette méthode est préférable à la première, car la correction est enregistrée dans un commit : elle est sauvegardée par Git, et vous pouvez donc la manipuler comme bon vous semble (voire la garder telle quelle si vous estimez au final qu’une fusion n’est pas la meilleure solution).

Méthode 3 — autosquash

Nous allons ici faire la même chose que la méthode 2, à ceci près que nous allons utiliser les aides fournies par Git.

  1. Enregistrez la correction dans un nouveau commit, dont le nom est « fixup! <nom du nommit à corriger> »
    • « git commit -m 'fixup! ...' »
    • Vous pouvez aussi utiliser « squash! » à la place de « fixup! »
  2. Lancer un rebase interactif avec l’option « autosquash »
    • « git rebase --interactive --autosquash <ref>^ »

L’option --autosquash a demandée à Git de placer le commit de correction au bon endroit, et a automatiquement demandé la fusion (en remplaçant pick par fixup ou squash).

Cela vous permet donc de ne pas avoir à bidouiller manuellement le fichier ouvert par Git.

Méthode 4 — commit --fixup

Nouvelle aide : l’option « fixup » de git commit.

  1. Enregistrez la correction dans un nouveau commit de fix
    • « git commit --fixup <ref> »
    • Vous pouvez aussi utiliser --squash
  2. Lancer un rebase interactif avec l’option « autosquash »
    • « git rebase --interactive --autosquash <ref>^ »

L’option --fixup (respectivement --squash) a demandée à Git de spécifier le bon nom de commit, commençant donc par « fixup! » (respectivement « squash! »)

Cette méthode est donc plus simple (seulement 2 appels) et limite le risque d’erreur, car laisse Git faire le boulot ingrat… Sympa non ?

Aplatir l’historique… ou pas

La commande git rebase saute les commits de merge par défaut. Elle aplatit donc l’historique, ce qui n’est pas toujours ce que l’on souhaite.

Sachez donc qu’il existe une option --preserve-merges (ou -p) qui change ce comportement.

Conclusion

La commande git rebase, même s’il ne s’agit que d’une commande de haut niveau (on pourrait refaire la même chose en se basant sur les autres commandes) fait partie des outils qui font la puissance de Git.

Posez-vous quand même des questions si vous avez besoin de l’utiliser. Est-ce vraiment une bonne chose de faire un rebase ?

Prochain article, git gibs, pour un peu de dissection :-)


  1. Même si, au final, on peut reproduire son comportement manuellement.