git rebase

Ce document contient une discussion approfondie sur la commande git rebase. La commande rebase a également été abordée sur les pages Configuration d'un dépôt et Réécriture de l'historique. Cette page examinera plus en détail la configuration et l'exécution de la commande git rebase. Les cas d'usage et les pièges courants du rebase seront couverts ici.

Le rebase est l'un des deux utilitaires Git spécialisé dans l'intégration des changements d'une branche à une autre. L'autre utilitaire d'intégration des changements est git merge. Le merge est toujours un enregistrement des changements vers l'avant. Alternativement, le rebase dispose de puissantes fonctionnalités de réécriture de l'historique. Pour une comparaison détaillée du merge et du rebase, consultez notre Guide du merging et du rebasage. Le rebase lui-même compte deux modes principaux : « manuel » et « interactif ». Nous couvrirons les différents modes de rebase plus en détail ci-dessous.

Qu'est-ce que git rebase ?

Le rebasage consiste à déplacer vers un nouveau commit de base ou à combiner une séquence de commits. Le rebasage est plus utile et facilement visible dans le contexte d'un workflow de branche de fonctionnalité. Vous pouvez visualiser le processus général comme suit :

Tutoriel Git : Git rebase

Au niveau du contenu, le rebase consiste à changer la base de votre branche d'un commit vers un autre, donnant l'illusion que vous avez créé votre branche à partir d'un commit différent. En interne, Git réalise cette tâche en créant de nouveaux commits et en les appliquant à la base spécifiée. Il est important de comprendre que, même si la branche semble identique, elle est composée de commits tout nouveaux.

Utilisation

La principale raison du rebasage est de maintenir un historique de projet linéaire. Par exemple, imaginez une situation où la branche master a évolué depuis que vous avez commencé à travailler sur une branche de fonctionnalité. Vous souhaitez obtenir les dernières mises à jour de la branche master dans votre branche de fonctionnalité, mais vous souhaitez garder l'historique de votre branche propre de sorte qu'il apparaisse comme si vous aviez travaillé dans la branche master la plus récente. Cela permettra par la suite d'effectuer un merge propre de votre branche de fonctionnalité dans la branche principale. Pourquoi souhaitons-nous conserver un « historique propre » ? Les avantages d'un historique propre deviennent tangibles lors de l'exécution d'opérations Git pour étudier l'introduction d'une régression. Un scénario plus réaliste serait :

  1. Un bug a été identifié dans la branche master. Une fonctionnalité qui fonctionnait parfaitement rencontre désormais des problèmes.
  2. Un développeur examine l'historique de la branche master avec git log. Grâce à « l'historique propre », il peut rapidement raisonner sur l'historique du projet.
  3. Le développeur ne peut déterminer quand le bug a été introduit avec git log, il exécute donc git bisect.
  4. Parce que l'historique Git est propre, git bisect dispose d'un ensemble de commits affinés à comparer lors de l'examen de la régression. Le développeur trouve rapidement le commit qui a introduit le bug et peut agir en conséquence.

Découvrez-en plus git log et git bisect sur leurs pages d'utilisation individuelles.

Vous avez deux options pour intégrer votre fonctionnalité dans la branche master : faire un merge directement ou rebaser, puis faire un merge. La première option génère un merge à trois branches et un commit de merge, alors que la deuxième entraîne un fast-forward merge et génère un historique parfaitement linéaire. Le schéma suivant illustre comment le rebasage sur la branche master facilite un fast-forward merge.

Git rebase : Branche sur master

Le rebasage est une méthode courante pour intégrer les changements en amont dans votre répertoire local. L'intégration des changements en amont avec Git merge génère un commit de merge superflu dès que vous voulez voir comment le projet a évolué. D'un autre côté, rebaser revient à dire : « Je veux baser mes changements sur ce que les autres ont fait ».

Ne pas rebaser l'historique public

Comme nous l'avons vu dans Réécriture de l'historique, vous ne devez jamais rebaser des commits qui ont été pushés vers un dépôt public. Le rebase remplacerait les anciens commits par les nouveaux, et ce serait comme si l'historique de votre projet avait brusquement disparu.

git rebase standard et git rebase interactif

Le rebase interactif Git désigne le moment où le rebase Git accepte un argument -- i. Ce « i » représente « Interactif ». Sans argument, la commande s'exécute en mode standard. Dans les deux cas, supposons que vous avez créé une branche de fonctionnalité distincte.

# Créez une branche de fonctionnalité basée sur la branche master
git checkout -b feature_branch master
# Éditez les fichiers
git commit -a -m "Adds new feature"

La commande git rebase en mode standard appliquera automatiquement les commits à votre branche de travail actuelle, puis à la pointe de la branche transférée.

git rebase 

 

Cela rebase automatiquement la branche courante sur <base>, qui peut être n'importe quel type de référence de commit (par exemple, un ID, un nom de branche, un tag ou une référence relative à HEAD).

Exécuter git rebase avec l'option -i démarre une session de rebasage interactive. Au lieu de déplacer aveuglément tous les commits vers la nouvelle base, le rebasage interactif vous permet de modifier des commits un à un au cours du processus. Vous pouvez ainsi nettoyer l'historique en supprimant, en séparant et en modifiant une série existante de commits. C'est comme Git commit --amend sous stéroïdes.

git rebase --interactive 

 

Cela rebase la branche courante sur <base>, mais utilise une session de rebasage interactive. Cette action ouvre un éditeur dans lequel vous pouvez entrer des commandes (décrites ci-dessous) pour chaque commit à rebaser. Ces commandes définissent comment des commits individuels vont être transférés vers la nouvelle base. Vous pouvez également réorganiser la liste des commits pour changer l'ordre des commits. Lorsque vous avez spécifié les commandes pour chaque commit dans le rebase, Git commence à relire les commits en appliquant les commandes de rebase. Les commandes d'édition de rebasage sont les suivantes :


pick 2231360 some old commit
pick ee2adc2 Adds new feature
# Rebasez 2cf755d..ee2adc2 onto 2cf755d (9 commandes)
#
# Commandes :
# p, pick = utilisez le commit
# r, reword = utilisez le commit, mais éditez le message de commit
# e, edit = utilisez le commit, mais arrêtez-vous pour apporter des changements
# s, squash = utilisez le commit, mais intégrez-le au commit précédent
# f, fixup = commande similaire à "squash", mais qui permet d'annuler le message de log de ce commit
# x, exec = exécutez la commande (le reste de la ligne) à l'aide de Shell 
# d, drop = supprimez le commit

Commandes rebase supplémentaires

Comme détaillé dans la page Réécriture de l'historique, le rebasage peut être utilisé pour modifier des commits anciens et multiples, des fichiers commités et des messages multiples. Bien qu'il s'agisse là des applications les plus fréquentes, git rebase dispose également d'options de commande supplémentaires utiles dans des applications plus complexes.

  • git rebase -- d signifie que durant la lecture, le commit sera supprimé du bloc de commits combinés final.
  • git rebase -- p laisse le commit en l'état. Il ne modifiera pas le message ou le contenu du commit, et le commit restera un commit individuel dans l'historique des branches.
  • git rebase -- x exécute un script Shell de ligne de commande sur chaque commit marqué lors de la lecture. Un exemple utile consisterait à exécuter la suite de tests de votre base de code sur des commits spécifiques, ce qui pourrait aider à identifier des régressions lors d'un rebase.

Récapitulatif

Le rebasage interactif vous donne le contrôle total sur l'apparence de votre historique de projet. Les développeurs ont ainsi une grande liberté pour enregistrer un historique « désordonné » en se concentrant sur la programmation, avant d'y revenir et de le nettoyer ultérieurement.

La plupart des développeurs aiment utiliser un rebase pour affiner une branche de fonctionnalité avant d'en faire un merge dans la base de code principale. Ils peuvent ainsi écraser les commits qui ne sont pas importants, supprimer ceux qui sont obsolètes et s'assurer que le reste est en ordre avant de faire des commits dans l'historique de projet « officiel ». Pour les autres, il semblera que la fonctionnalité a été entièrement développée dans une seule série de commits bien planifiés.

Le vrai pouvoir du rebasage interactif peut être constaté dans l'historique de la branche master obtenue. Pour les autres, vous passerez pour un développeur brillant qui a implémenté la nouvelle fonctionnalité avec le nombre parfait de commits du premier coup. Le rebasage interactif permet ainsi de maintenir la propreté et la cohérence de l'historique d'un projet.

Options de configuration

Quelques propriétés de rebase peuvent être définies avec git config. Ces options modifieront l'apparence de la sortie git rebase.

  • rebase.stat : un booléen qui est par défaut défini sur « false ». L'option bascule l'affichage du contenu visuel diffstat qui montre ce qui a changé depuis le dernier rebase.
  • rebase.autoSquash : une valeur booléenne qui modifie le comportement --autosquash.
  • rebase.missingCommitsCheck : Peut être défini sur plusieurs valeurs qui changent le comportement du rebase autour des commits manquants.
warn Affiche un sortie d'avertissement en mode interactif qui prévient de la suppression de commits

error

Arrête le rebase et imprime des messages d'avertissement de suppression de commits

ignore

Défini par défaut, il ignore tout avertissement lié à l'absence de commit
  • rebase.instructionFormat : une chaîne de format git log qui sera utilisée pour formater l'affichage du rebase interactif

Application de rebase avancé

L'argument de ligne de commande --onto peut être transmis à git rebase. Dans le mode git rebase --onto, la commande se développe en :

 git rebase --onto  

La commande --onto active une forme ou un rebase plus puissant qui permet de transmettre des réfs spécifiques qui deviendront des pointes de rebase.
Supposons que nous ayons un dépôt avec des branches comme :


o---o---o---o---o master
\
o---o---o---o---o featureA
\
o---o---o featureB

La branche featureB est basée sur la branche featureA. Toutefois, nous réalisons que featureB ne dépend pas des changements dans la fonctionnalité A et pourrait être dérivée de la branche master.

 git rebase --onto master featureA featureB

featureA est le <oldbase>. master devient <newbase>, et featureB est référencé pour ce vers quoi l'élément HEAD de <newbase> pointera. Les résultats sont alors les suivants :


o---o---o featureB
/
o---o---o---o---o master
\
o---o---o---o---o featureA

Comprendre les dangers du rebase

Lorsque vous travaillez avec git rebase, vous devez tenir compte d'une mise à jour : les conflits de merge peuvent devenir plus fréquents durant un workflow de rebase. Cela se produit si vous disposez d'une branche de longue durée qui s'est éloignée de la branche master. En fin de compte, vous souhaiterez effectuer un rebase avec la branche master, et à ce moment-là, elle peut contenir beaucoup de nouveaux commits avec lesquels les changements de votre branche peuvent entrer en conflit. Une solution facile consiste à rebaser fréquemment votre branche avec la branche master et à faire des commits plus fréquents. Les arguments de ligne de commande --continue et --abort peuvent être transmis à git rebase pour faire avancer ou réinitialiser le rebase en cas de conflits.

Un point plus sérieux à prendre en compte avec la commande rebase concerne la perte de commits lors de la réécriture de l'historique interactif. L'exécution du rebase en mode interactif et de sous-commandes comme squash ou drop supprimera les commits du journal immédiat de votre branche. À première vue, les commits peuvent sembler définitivement partis. Cependant, il est possible de restaurer ces commits et d'annuler l'ensemble du rebase avec git reflog. Pour en savoir plus sur l'utilisation de git reflog afin de retrouver des commits perdus, consultez notre page de documentation Git reflog.

La commande git rebase en elle-même n'est pas si dangereuse. Les dangers réels surviennent lors de l'exécution de rebases interactifs de réécriture d'historique et de pushs forcés des résultats vers une branche distante partagée par d'autres utilisateurs. C'est un modèle qui devrait être évité, car il est susceptible d'écraser le travail des autres utilisateurs distants lorsqu'ils font un pull.

Récupération du rebase en amont

Si un autre utilisateur a fait un rebase, puis un push de force vers la branche sur laquelle vous faites un commit, une opération git pull écrase tous les commits que vous avez basés sur cette branche précédente avec la pointe qui a été pushée en force. Heureusement, en utilisant git reflog, vous pouvez obtenir le reflog de la branche distante. Sur le reflog de la branche distante, vous pouvez trouver une réf avant qu'elle soit rebasée. Vous pouvez ensuite rebaser votre branche par rapport à cette réf distante à l'aide de l'option --onto comme indiqué ci-dessus dans la section Application avancée de rebase.

Summary

Dans cet article, nous avons couvert l'utilisation de git rebase. Nous avons discuté de cas d'usage de base et avancés, ainsi que d'exemples plus poussés. Voici certains des principaux sujets de discussion :

  • Comparaison des modes standard et interactif de git rebase
  • options de configuration de la commande git rebase
  • git rebase --onto
  • Commits perdus de la commande git rebase

Nous avons examiné l'utilisation de git rebase avec d'autres outils comme git reflog, git fetch et git push. Consultez leurs pages respectives pour en savoir plus.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant