Réécriture de l'historique avec Git Rebase

Dans le flux de travail Git fondamental, vous développez une nouvelle fonctionnalité dans une branche de sujet dédiée, puis vous la fusionnez dans une branche de production une fois celle-ci terminée. Cela fait fusionner un outil intégral pour combiner des branches. Cependant, ce n'est pas le seul que propose Git.

Combinaison de branches en les fusionnant

Comme alternative au scénario ci-dessus, vous pouvez combiner les branches avec le git rebase commander. Au lieu de lier les branches avec un commit de fusion, le rebasement déplace toute la branche de fonctionnalité vers la pointe de maîtriser comme indiqué ci-dessous.

Combinaison de branches avec git rebase

Cela sert le même objectif que fusionner, intégrer des commits de différentes branches. Mais il y a deux raisons pour lesquelles nous pourrions vouloir opter pour un rebase plutôt que pour une fusion:

  • Il en résulte un historique de projet linéaire.
  • Cela nous donne la possibilité de nettoyer les commits locaux.

Dans ce tutoriel, nous allons explorer ces deux cas d’utilisation courants de git rebase. Malheureusement, les avantages de git rebase venir à un compromis. Utilisé de manière incorrecte, il peut s’agir de l’une des opérations les plus dangereuses que vous puissiez effectuer dans un référentiel Git. Donc, nous allons également examiner de près les dangers de rebasement.

Conditions préalables

Ce tutoriel suppose que vous maîtrisiez les commandes Git de base et les workflows de collaboration. Vous devez être à l'aise pour préparer et créer des instantanés, développer des fonctionnalités dans des branches isolées, fusionner des branches et pousser / extraire des branches vers / depuis des référentiels distants..

1. Revoir une histoire linéaire

Le premier cas d'utilisation que nous allons explorer implique une histoire de projet divergente. Considérez un référentiel dans lequel votre branche de production a évolué pendant que vous développiez une fonctionnalité:

Pour rebaser le fonctionnalité branche sur le maîtriser branche, vous exécuterez les commandes suivantes:

fonctionnalité git checkout maître git rebase

Cette greffe le fonctionnalité branche de son emplacement actuel à la pointe de la maîtriser branche:

Il existe deux scénarios dans lesquels vous souhaiteriez procéder. Premièrement, si la fonctionnalité s’appuie sur les nouveaux commits de maîtriser, il y aurait maintenant accès. Deuxièmement, si la fonctionnalité était complète, elle serait désormais configurée pour une fusion rapide vers l’avenir. maîtriser. Dans les deux cas, le changement de base donne une histoire linéaire, alors que fusionner entraînerait des fusions de fusion inutiles.

Par exemple, considérons ce qui se passerait si vous intégriez les commits en amont avec une fusion au lieu d'une base:

Fonctionnalité Git Checkout Maître de fusion Git 

Cela nous aurait donné un engagement de fusion supplémentaire dans le fonctionnalité branche. De plus, cela se produirait chaque fois que vous voudriez incorporer des commits en amont dans votre fonctionnalité. Finalement, l'historique de votre projet serait encombré de commits de fusion sans signification.

Intégration des commits en amont avec une fusion

Ce même avantage peut être vu lors de la fusion dans l'autre sens. Sans rebase, intégrant le fini fonctionnalité branche en maîtriser nécessite un commit de fusion. Bien qu'il s'agisse en réalité d'un commit de fusion significatif (en ce sens qu'il représente une fonctionnalité terminée), l'historique résultant est rempli de forks:

Intégration d'une fonctionnalité complétée à une fusion

Lorsque vous rebasez avant de fusionner, Git peut effectuer une avance rapide. maîtriser à la pointe de fonctionnalité. Vous trouverez une histoire linéaire de la progression de votre projet dans le journal git sortie-les commets dans fonctionnalité sont soigneusement regroupés au-dessus des commits en maîtriser. Ce n’est pas nécessairement le cas lorsque les branches sont liées par un commit de fusion..

Rebasing avant de fusionner

Résoudre les conflits

Quand tu cours git rebase, Git prend chaque commit de la branche et les déplace un par un sur la nouvelle base. Si l'un de ces commits modifie la ou les mêmes lignes de code que le commuté en amont, il en résultera un conflit..

le fusionner Cette commande vous permet de résoudre tous les conflits de la branche à la fin de la fusion, ce qui est l’un des principaux objectifs d’un commit de fusion. Cependant, cela fonctionne un peu différemment lorsque vous changez de base. Les conflits sont résolus sur une base par engagement. Donc si git rebase trouve un conflit, il arrêtera la procédure de rebasement et affichera un message d'avertissement:

Fusion automatique du fichier readme.txt CONFLICT (contenu): conflit de fusion dans le fichier readme.txt Échec de la fusion des modifications… Une fois ce problème résolu, exécutez "git rebase --continue". Si vous préférez ignorer ce correctif, exécutez à la place "git rebase --skip". Pour extraire la branche d'origine et arrêter le rebasement, lancez "git rebase --abort". 

Visuellement, voici à quoi ressemble votre historique de projet git rebase rencontre un conflit:

Les conflits peuvent être inspectés en exécutant statut git. La sortie ressemble beaucoup à un conflit de fusion:

Chemins sans fusion: (utilisez "git reset HEAD … "Pour décompresser) (utilisez" git add … "Pour marquer la résolution) à la fois modifiés: readme.txt aucune modification ajoutée à commettre (utilisez" git add "et / ou" git commit -a ") 

Pour résoudre le conflit, ouvrez le fichier en conflit (lisez-moi.txt dans l'exemple ci-dessus), recherchez les lignes affectées et modifiez-les manuellement pour obtenir le résultat souhaité. Indiquez ensuite à Git que le conflit est résolu en organisant le fichier:

git add readme.txt 

Notez que c’est exactement la même façon de marquer un fusionner conflit comme résolu. Mais souvenez-vous que vous êtes en train de rebaser. Vous ne voulez pas oublier le reste des commits qui doivent être déplacés. La dernière étape consiste à dire à Git de finir de rebasonner avec le --continuer option:

git rebase --continue 

Cela déplacera le reste des commits, un par un, et si d'autres conflits surviennent, vous devrez répéter ce processus encore une fois..

Si vous ne voulez pas résoudre le conflit, vous pouvez opter pour la --sauter ou --avorter drapeaux. Ce dernier est particulièrement utile si vous n’avez aucune idée de ce qui se passe et que vous voulez juste retourner à la sécurité..

# Ignore le commit qui a causé le conflit git rebase --skip # Abandonne toute la rebase et revient à la planche à dessin git rebase --abort 

2. Modification de la base pour nettoyer les commits locaux

Jusqu'à présent, nous utilisons seulement git rebase déplacer des branches, mais c'est beaucoup plus puissant que cela. En passant le -je drapeau, vous pouvez commencer une session de rebasage interactive. Le rebasage interactif vous permet de définir précisément comment chaque validation sera déplacée vers la nouvelle base. Cela vous donne la possibilité de nettoyer l'historique d'une fonctionnalité avant de la partager avec d'autres développeurs..

Par exemple, supposons que vous ayez fini de travailler sur votre fonctionnalité branche et vous êtes prêt à l'intégrer dans maîtriser. Pour commencer une session de rebasage interactive, exécutez la commande suivante:

fonctionnalité git checkout git rebase -i master 

Cela ouvrira un éditeur contenant tous les commits de fonctionnalité qui sont sur le point d'être déplacés:

pick 5c43c2b [Description du commit le plus ancien] pick b8f3240 [Description du 2e commit le plus ancien] pick c069f4a [Description du coup le plus récent] 

Cette liste définit ce que le fonctionnalité branche va ressembler après la rebase. Chaque ligne représente un commit et le choisir commande avant chaque hachage de validation définit ce qui va lui arriver pendant la base. Notez que les commits sont listés du plus ancien au plus récent. En modifiant cette liste, vous obtenez un contrôle complet sur l'historique de votre projet..

Si vous souhaitez modifier l'ordre des commits, réorganisez simplement les lignes. Si vous voulez changer le message d'un commit, utilisez le bouton reformuler commander. Si vous voulez combiner deux commits, changez le choisir commande à écraser. Cela transférera tous les changements de ce commit dans celui qui le précède. Par exemple, si vous écrasez le deuxième commit dans la liste ci-dessus, le fonctionnalité branche ressemblerait à ceci après la sauvegarde et la fermeture de l'éditeur:

Réduire le 2e commit avec une base interactive

le modifier le commandement est particulièrement puissant. Quand il atteindra la validation spécifiée, Git mettra en pause la procédure de rebase, un peu comme lorsqu'il rencontre un conflit. Cela vous donne la possibilité de modifier le contenu du commit avec git commit --amend ou même ajouter plus de commits avec la norme git ajouter/git commit commandes. Tous les nouveaux commits que vous ajoutez feront partie de la nouvelle branche..

Le reclassement interactif peut avoir un impact profond sur votre flux de travail de développement. Au lieu de craindre de diviser vos modifications en commits encapsulés, vous pouvez vous concentrer sur l'écriture de votre code. Si vous avez finalement commis ce qui devrait être un seul changement en quatre instantanés distincts, alors ce ne sera pas un historique de réécriture de problème avec git rebase -i et les écraser tous en un seul engagement significatif.

3. Dangers du changement de base

Maintenant que vous avez compris git rebase, nous pouvons parler de quand ne pas l'utiliser. En interne, le changement de base ne déplace pas les commits vers une nouvelle branche. Au lieu de cela, il crée de nouveaux commits contenant les modifications souhaitées. Dans cet esprit, le rebasement est mieux visualisé comme suit:

Après la rebase, les commits en fonctionnalité aura différents hashes de commettre. Cela signifie que nous n'avons pas simplement repositionné une branche: nous avons littéralement réécrit l'historique de nos projets. Ceci est un effet secondaire très important de git rebase.

Lorsque vous travaillez seul sur un projet, la réécriture de l'historique n'est pas un gros problème. Cependant, dès que vous commencez à travailler dans un environnement collaboratif, cela peut devenir très dangereux. Si vous réécrivez les commits que d’autres développeurs utilisent (par exemple, les commits sur le maîtriser branche), on dirait que ces commits ont disparu la prochaine fois qu’ils tentent de tirer votre travail. Il en résulte un scénario confus qui est difficile à récupérer à partir de.

Dans cet esprit, vous ne devriez jamais réabaisser les commits qui ont été placés dans un référentiel public, à moins que vous ne soyez sûrs que personne n'a basé leur travail sur eux..

Conclusion

Ce tutoriel a présenté les deux cas d’utilisation les plus courants de git rebase. Nous avons beaucoup parlé de déplacer les branches, mais gardez à l’esprit que le changement de base revient en réalité à contrôler l’historique de vos projets. Le pouvoir de réécrire s’engage après coup vous permet de vous concentrer sur vos tâches de développement au lieu de décomposer votre travail en instantanés isolés..

Notez que le rebasement est un ajout totalement facultatif à votre boîte à outils Git. Vous pouvez toujours faire tout ce dont vous avez besoin avec un bon vieux fusionner commandes. En effet, cela est plus sûr car cela évite la possibilité de réécrire l'histoire publique. Cependant, si vous comprenez les risques, git rebase peut être un moyen beaucoup plus propre d'intégrer les branches par rapport à la fusion des commits.