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 :
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.
Ressource connexe
Fiche de révision sur Git
DÉCOUVRIR LA SOLUTION
Découvrir Git avec Bitbucket Cloud
Utilisation
La principale raison du rebasage est de maintenir un historique de projet linéaire. Par exemple, imaginez une situation où la branche principale a évolué depuis que vous avez commencé à travailler sur une branche de fonctionnalité. Vous souhaitez obtenir les dernières mises à jour de la branche principale 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 main 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 principale. Une fonctionnalité qui fonctionnait parfaitement rencontre désormais des problèmes.
2. A developer examines the history of the main branch using git log
because of the "clean history" the developer is quickly able to reason about the history of the project.
3. The developer can not identify when the bug was introduced using git log
so the developer executes a git bisect
.
4. Because the git history is clean, git bisect
has a refined set of commits to compare when looking for the regression. The developer quickly finds the commit that introduced the bug and is able to act accordingly.
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 principale : 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 principale facilite un fast-forward merge.
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 vs git rebase interactive
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.
# Create a feature branch based off of main
git checkout -b feature_branch main
# Edit files
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 <base>
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 <base>
Cela rebase la branche actuelle sur <base>
, mais utilise une session de rebase interactive. Cela 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 petre 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
# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove 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 principale 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.
| Affiche un sortie d'avertissement en mode interactif qui prévient de la suppression de commits |
| Arrête le rebase et imprime des messages d'avertissement de suppression de commits |
| Défini par défaut, il ignore tout avertissement lié à l'absence de commit |
rebase.instructionFormat :
une chaîne de formatgit 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 <newbase> <oldbase>
The --onto
command enables a more powerful form or rebase that allows passing specific refs to be the tips of a rebase. Let’s say we have an example repo with branches like:
o---o---o---o---o main
\
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 featureA et pourrait être dérivée de la branche principale.
git rebase --onto main featureA featureB
featureA est le <oldbase>
. main
devient le <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 main
\
o---o---o---o---o featureA
Comprendre les dangers du rebase
Lorsque vous travaillez avec git rebase, soyez prudent : les conflits de merge peuvent devenir plus fréquents durant un workflow de rebase. Cela se produit si vous disposez d'une branche au long cours qui s'est éloignée de la branche principale. En fin de compte, vous souhaiterez effectuer un rebase avec la branche principale, 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 principale 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.
Résumé
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
We looked at git rebase
usage with other tools like git reflog, git fetch, and git push. Visit their corresponding pages for further information.
Partager cet article
Thème suivant
Lectures recommandées
Ajoutez ces ressources à vos favoris pour en savoir plus sur les types d'équipes DevOps, ou pour les mises à jour continues de DevOps chez Atlassian.