Quand utilisez-vous Git rebase au lieu de Git merge?


1549

Quand est-il recommandé d'utiliser le rebase Git par rapport à la fusion Git?

Dois-je encore fusionner après un rebasage réussi?




6
Un problème avec les gens qui aiment utiliser le rebase est qu'il les dissuade de pousser leur code régulièrement. Donc, vouloir une histoire propre les empêche de partager leur code, ce qui, je pense, est plus important.
static_rtti

9
@static_rtti: Ce n'est tout simplement pas vrai. Vous utilisez un flux basé sur une rebase incorrect s'il vous empêche de pousser régulièrement vos modifications.
juzzlin

5
Il est vraiment dommage que la réponse d' Andrew Arnott et celle de Pace n'aient pas été publiées plus tôt, car ils répondent à cette question de manière plus complète que les réponses précédentes qui avaient déjà accumulé de nombreux votes.
Mark Booth

Réponses:


1136

Version courte

  • La fusion prend toutes les modifications dans une branche et les fusionne dans une autre branche dans un commit.
  • Rebase dit que je veux que le point auquel je me ramifie passe à un nouveau point de départ

Alors, quand utilisez-vous l'un ou l'autre?

Fusionner

  • Supposons que vous ayez créé une branche dans le but de développer une seule fonctionnalité. Lorsque vous souhaitez ramener ces modifications à master, vous voulez probablement fusionner (vous ne vous souciez pas de maintenir tous les validations provisoires).

Rebase

  • Un deuxième scénario serait si vous commenciez à faire du développement et qu'un autre développeur apportait un changement indépendant. Vous souhaiterez probablement extraire puis rebaser pour baser vos modifications à partir de la version actuelle à partir du référentiel.

105
@Rob a mentionné le maintien de validations provisoires lors de la fusion. Je crois que par défaut, la fusion de la branche B (une branche de fonctionnalité sur laquelle vous avez travaillé) dans la branche M (la branche principale) créera une validation dans M pour chaque validation effectuée en B depuis la divergence des deux. Mais si vous fusionnez en utilisant l'option --squash, toutes les validations effectuées sur la branche B seront "regroupées" et fusionnées en une seule validation sur la branche M, gardant le journal de votre branche principale agréable et propre. Le squashing est probablement ce que vous voulez si vous avez de nombreux développeurs travaillant de manière indépendante et fusionnant à nouveau dans master.
spaaarky21

19
Je pense que l'hypothèse de @ spaaarky21 concernant la fusion n'est pas correcte. Si vous fusionnez une branche B dans le maître M, il n'y aura qu'une seule validation sur M (même si B a plusieurs validations), que vous utilisiez une fusion simple ou --squash. Ce que --squash fera, c'est d'éliminer la référence à B en tant que parent. Une bonne visualisation est ici: syntevo.com/smartgithg/howtos.html?page=workflows.merge
jpeskin

14
@jpeskin Ce n'est pas ce que je vois. Je viens de faire un test rapide pour vérifier. Créez un répertoire avec un fichier texte, initun nouveau adddépôt , le fichier et commit. Extraire une nouvelle branche de fonctionnalité ( checkout -b feature.) Modifiez le fichier texte, validez et répétez afin qu'il y ait deux nouvelles validations dans la branche de fonctionnalité. Ensuite checkout masteret merge feature. Dans log, je vois mon commit initial sur master, suivi des deux qui ont été fusionnés depuis la fonctionnalité. Si vous merge --squash feature, la fonctionnalité est fusionnée dans master mais non validée, le seul nouveau commit sur master sera celui que vous réaliserez vous-même.
spaaarky21

21
@ spaaarky21 On dirait que nous avons tous les deux à moitié raison. Lorsqu'une fusion rapide est possible (comme dans votre exemple), git inclura par défaut toutes les validations dans la branche de fonctionnalité B (ou comme vous le suggérez, vous pouvez utiliser --squash pour combiner en une seule validation). Mais dans le cas où il y a deux branches divergentes M et B que vous fusionnez, git n'inclura pas toutes les validations individuelles de la branche B si elles sont fusionnées dans M (que vous utilisiez ou non --squash).
jpeskin

6
Pourquoi est-ce que le "(vous ne vous souciez pas de maintenir tous les engagements provisoires)" est de côté dans cette réponse? Cela n'avait aucun sens en 2009 et cela n'a plus de sens maintenant. De plus, vous ne voudrez sûrement rebaser que si un autre développeur a apporté les modifications connexes dont vous aviez besoin - s'il a apporté des modifications non liées, votre branche de fonctionnalités devrait fusionner facilement sans conflits de toute façon, et votre historique serait conservé.
Mark Booth

372

C'est simple. Avec rebase, vous dites d'utiliser une autre branche comme nouvelle base pour votre travail.

Si vous avez, par exemple, une branche master, vous créez une branche pour implémenter une nouvelle fonctionnalité, et dites que vous la nommez cool-feature, bien sûr la branche principale est la base de votre nouvelle fonctionnalité.

Maintenant, à un certain moment, vous souhaitez ajouter la nouvelle fonctionnalité que vous avez implémentée dans la masterbranche. Vous pouvez simplement basculer masteret fusionner la cool-featurebranche:

$ git checkout master
$ git merge cool-feature

Mais de cette façon, un nouveau commit factice est ajouté. Si vous voulez éviter l'histoire des spaghettis, vous pouvez rebaser :

$ git checkout cool-feature
$ git rebase master

Et puis fusionnez-le dans master:

$ git checkout master
$ git merge cool-feature

Cette fois, puisque la branche de sujet a les mêmes validations de master plus les validations avec la nouvelle fonctionnalité, la fusion ne sera qu'une avance rapide.


31
but this way a new dummy commit is added, if you want to avoid spaghetti-history- comment c'est mauvais?
ア レ ッ ク ス

6
De plus, le drapeau de fusion --no-ff est très très utile.
Aldo 'xoen' Giambelluca

3
@ ア レ ッ ク ス comme le dit l'utilisateur Sean Schofielddans un commentaire: "Rebase est également agréable car une fois que vous avez finalement fusionné vos données dans master (ce qui est trivial comme déjà décrit), vous l'avez placé en" haut "de l'historique de vos validations. les projets où les fonctionnalités peuvent être écrites mais fusionnées plusieurs semaines plus tard, vous ne voulez pas simplement les fusionner dans le master car elles sont "bourrées" dans le chemin du master dans l'histoire. Personnellement, j'aime pouvoir faire git log et voir cette fonctionnalité récente juste en haut. "Notez que les dates de commit sont préservées - rebase ne change pas ces informations."
Adrien Be

4
Je pense qu'il faut le répéter ici - rappelez - vous que tous ces termes ( merge, rebase, fast-forward, etc.) font référence à des manipulations spécifiques d'un graphe orienté acyclique. Ils deviennent plus faciles à raisonner avec ce modèle mental à l'esprit.
Roy Tinker

10
@Aldo Il n'y a rien de "propre" ou de "bien rangé" dans une histoire rebasée. C'est généralement sale et à mon humble avis horrible parce que vous n'avez aucune idée de ce qui s'est vraiment passé. L'histoire Git la plus «propre» est celle qui s'est réellement produite. :)
Marnen Laibow-Koser

269

Pour compléter ma propre réponse mentionnée par TSamper ,

  • un rebase est assez souvent une bonne idée à faire avant une fusion, car l'idée est que vous intégriez dans votre branche Yle travail de la branche Bsur laquelle vous allez fusionner.
    Mais encore une fois, avant de fusionner, vous résolvez tout conflit dans votre branche (par exemple: "rebase", comme dans "rejouer mon travail dans ma branche à partir d'un point récent de la branche B).
    Si cela est fait correctement, la fusion ultérieure de votre branche vers la branche Bpeut être en avance rapide.

  • une fusion affecte directement la branche de destination B , ce qui signifie que les fusions devraient être triviales, sinon cette branche Bpeut être longue pour revenir à un état stable (il est temps pour vous de résoudre tous les conflits)


le point de fusionner après un rebase?

Dans le cas que je décris, je rebase B sur ma branche, juste pour avoir l'opportunité de rejouer mon travail depuis un point plus récent B, mais en restant dans ma branche.
Dans ce cas, une fusion est toujours nécessaire pour intégrer mon travail "rejoué" B.

L'autre scénario ( décrit dans Git Ready par exemple), consiste à importer votre travail directement Bvia un rebase (qui conserve tous vos bons commits, ou même vous donne la possibilité de les réorganiser via un rebase interactif).
Dans ce cas (où vous rebasez tout en étant dans la branche B), vous avez raison: aucune autre fusion n'est nécessaire:

Un arbre Git par défaut lorsque nous n'avons pas fusionné ni rebasé

rebase1

on obtient en rebasant:

rebase3

Ce deuxième scénario concerne: comment puis-je récupérer une nouvelle fonctionnalité dans le maître.

Mon propos, en décrivant le premier scénario de rebase, est de rappeler à tout le monde qu'un rebase peut également être utilisé comme une étape préliminaire à cela (c'est-à-dire "remettre la nouvelle fonctionnalité dans le maître").
Vous pouvez utiliser le rebasage pour amener d'abord le maître "dans" la branche de nouvelles fonctionnalités: le rebasage rejouera les validations de nouvelles fonctionnalités à partir de HEAD master, mais toujours dans la branche des nouvelles fonctionnalités, déplaçant efficacement le point de départ de votre branche d'un ancien commit principal vers HEAD-master.
Cela vous permet de résoudre tous les conflits dans votre branche (ce qui signifie, de manière isolée, tout en permettant au maître de continuer à évoluer en parallèle si votre étape de résolution de conflit prend trop de temps).
Ensuite, vous pouvez passer en master et fusionner new-feature(ou rebaser new-featuresur mastersi vous souhaitez conserver les commits effectués dans votrenew-feature branche).

Donc:

  • "rebase vs merge" peut être considéré comme deux façons d'importer une œuvre, par exemple master.
  • Mais «rebaser puis fusionner» peut être un flux de travail valide pour résoudre d'abord les conflits de manière isolée, puis ramener votre travail.

17
fusionner après rebase est une avance rapide triviale sans avoir à résoudre les conflits.
obecalp

4
@obelcap: En effet, c'est une sorte d'idée: vous prenez tout le problème-conflit dans votre environnement (rebase master au sein de votre branche new-feature), puis co master, fusionnez new-feature: 1 pico-seconde (fast- avant) si le maître n'a eu aucune évolution
VonC

27
Rebase est également agréable car une fois que vous avez finalement fusionné vos données dans master (ce qui est trivial comme déjà décrit), vous les avez placées au "haut" de votre historique de commit. Sur les projets plus importants où les fonctionnalités peuvent être écrites mais fusionnées plusieurs semaines plus tard, vous ne voulez pas simplement les fusionner dans le master car elles sont "bourrées" dans le chemin du master dans l'histoire. Personnellement, j'aime pouvoir faire git log et voir cette fonctionnalité récente juste en haut. Notez que les dates de validation sont préservées - le rebasage ne modifie pas ces informations.
Sean Schofield

3
@Joe: mentalement, vous dites "rejouer mes modifications (effectuées isolément dans ma branche privée) par-dessus cette autre branche, mais laissez-moi dans ma branche privée une fois le rebase terminé". C'est une bonne occasion de nettoyer l'histoire locale, en évitant les «commissions de points de contrôle», les coupures cassées et les résultats de blâme incorrects. Voir "Git workflow": sandofsky.com/blog/git-workflow.html
VonC

4
@scoarescoare la clé est de voir comment vos changements locaux sont compatibles en plus de la dernière branche en amont. Si l'un de vos commit introduit un conflit, vous le verrez tout de suite. Une fusion n'introduit qu'un seul commit (fusionné), ce qui pourrait déclencher de nombreux conflits sans un moyen facile de voir lequel, parmi vos propres commits locaux, a ajouté ledit conflit. Ainsi, en plus d'un historique plus propre, vous obtenez une vue plus précise des modifications que vous introduisez, commit by commit (rejoué par la rebase), par opposition à toutes les modifications introduites par la branche en amont (déversées dans une seule fusion).
VonC

229

TL; DR

En cas de doute, utilisez la fusion.

Réponse courte

Les seules différences entre un rebase et une fusion sont:

  • La structure arborescente résultante de l'historique (généralement visible uniquement lorsque l'on regarde un graphe de validation) est différente (l'une aura des branches, l'autre non).
  • La fusion crée généralement une validation supplémentaire (par exemple, un nœud dans l'arborescence).
  • Fusionner et rebaser traitera les conflits différemment. Rebase présentera les conflits un commit à la fois où la fusion les présentera tous en même temps.

Donc, la réponse courte est de choisir un rebase ou une fusion en fonction de ce à quoi vous voulez que votre historique ressemble .

Longue réponse

Il y a quelques facteurs à considérer lors du choix de l'opération à utiliser.

La branche dont vous obtenez les modifications est-elle partagée avec d'autres développeurs en dehors de votre équipe (par exemple, open source, public)?

Si oui, ne rebasez pas. Rebase détruit la branche et ces développeurs auront des référentiels cassés / incohérents à moins qu'ils n'utilisent git pull --rebase. C'est un bon moyen de déranger rapidement d'autres développeurs.

Quelle est la compétence de votre équipe de développement?

Rebase est une opération destructrice. Cela signifie que si vous ne l'appliquez pas correctement, vous risquez de perdre du travail engagé et / ou de casser la cohérence des référentiels d'autres développeurs.

J'ai travaillé dans des équipes où les développeurs venaient tous d'une époque où les entreprises pouvaient se permettre un personnel dédié pour gérer les succursales et les fusions. Ces développeurs ne connaissent pas grand-chose à Git et ne veulent pas en savoir beaucoup. Dans ces équipes, je ne risquerais pas de recommander le rebasage pour une raison quelconque.

La branche elle-même représente-t-elle des informations utiles

Certaines équipes utilisent le modèle de branche par fonctionnalité où chaque branche représente une fonctionnalité (ou correction de bogue, ou sous-fonctionnalité, etc.) Dans ce modèle, la branche aide à identifier les ensembles de validations associées. Par exemple, on peut rapidement annuler une fonctionnalité en annulant la fusion de cette branche (pour être juste, c'est une opération rare). Ou différenciez une fonctionnalité en comparant deux branches (plus courantes). Rebase détruirait la branche et ce ne serait pas simple.

J'ai également travaillé sur des équipes qui utilisaient le modèle de branche par développeur (nous y sommes tous allés). Dans ce cas, la branche elle-même ne transmet aucune information supplémentaire (le commit a déjà l'auteur). Il n'y aurait aucun mal à rebaser.

Pourriez-vous annuler la fusion pour une raison quelconque?

La restauration (comme pour l'annulation) d'un rebase est considérablement difficile et / ou impossible (si le rebase avait des conflits) par rapport à l'annulation d'une fusion. Si vous pensez qu'il y a une chance que vous souhaitiez revenir en arrière, utilisez la fusion.

Travaillez-vous en équipe? Si oui, êtes-vous prêt à adopter une approche tout ou rien sur cette branche?

Les opérations de rebase doivent être tirées avec un correspondant git pull --rebase. Si vous travaillez seul, vous pourrez peut-être vous souvenir de ce que vous devez utiliser au moment opportun. Si vous travaillez en équipe, cela sera très difficile à coordonner. C'est pourquoi la plupart des workflows de rebase recommandent d'utiliser rebase pour toutes les fusions (et git pull --rebasepour tous les pulls).

Mythes courants

La fusion détruit l'histoire (les courges se commettent)

En supposant que vous ayez la fusion suivante:

    B -- C
   /      \
  A--------D

Certaines personnes diront que la fusion "détruit" l'historique de validation car si vous ne regardiez que le journal de la branche principale (A - D), vous manqueriez les messages de validation importants contenus dans B et C.

Si c'était vrai, nous n'aurions pas de questions comme celle-ci . Fondamentalement, vous verrez B et C à moins que vous ne demandiez explicitement de ne pas les voir (en utilisant --first-parent). C'est très facile à essayer par vous-même.

Rebase permet des fusions plus sûres / plus simples

Les deux approches fusionnent différemment, mais il n'est pas clair que l'une est toujours meilleure que l'autre et cela peut dépendre du flux de travail du développeur. Par exemple, si un développeur a tendance à s'engager régulièrement (par exemple, il peut s'engager deux fois par jour lors de la transition du travail à la maison), il pourrait y avoir beaucoup de validations pour une branche donnée. Beaucoup de ces validations peuvent ne pas ressembler au produit final (j'ai tendance à refactoriser mon approche une ou deux fois par fonctionnalité). Si quelqu'un d'autre travaillait sur un domaine de code connexe et essayait de rebaser mes modifications, cela pourrait être une opération assez fastidieuse.

Rebase est plus cool / plus sexy / plus professionnel

Si vous aimez alias rmpour rm -rf"gagner du temps", alors peut-être que rebaser est pour vous.

Mes deux centimes

Je pense toujours qu'un jour je tomberai sur un scénario où Git rebase est l'outil génial qui résout le problème. Tout comme je pense que je vais rencontrer un scénario où Git reflog est un outil génial qui résout mon problème. Je travaille avec Git depuis plus de cinq ans maintenant. Ce n'est pas arrivé.

Les histoires désordonnées n'ont jamais vraiment été un problème pour moi. Je ne lis jamais mon histoire de commit comme un roman passionnant. La plupart du temps, j'ai besoin d'une histoire, je vais quand même utiliser Git blame ou Git bissect. Dans ce cas, la validation de la fusion est en fait utile pour moi, car si la fusion a introduit le problème, c'est une information significative pour moi.

Mise à jour (4/2017)

Je me sens obligé de mentionner que j'ai personnellement adouci l'utilisation de rebase bien que mes conseils généraux soient toujours valables. J'ai récemment beaucoup interagi avec le projet Angular 2 Material . Ils ont utilisé rebase pour garder un historique de commit très propre. Cela m'a permis de voir très facilement quel commit a corrigé un défaut donné et si ce commit a été inclus ou non dans une version. Il constitue un excellent exemple d'utilisation correcte du rebasage.


5
Doit être la réponse validée.
Mik378

C'est certainement la meilleure réponse. Surtout avec la remarque clarifiée dans la dernière mise à jour. Il permet de garder l'historique Git propre et clair, mais utilisez-le en toute sécurité.
zquintana

5
J'aime surtout cette réponse. Mais: Rebase ne fait pas une histoire "propre". Cela fait une histoire plus linéaire, mais ce n'est pas du tout la même chose, car qui sait maintenant beaucoup de "saleté" que chaque commit cache? L'histoire Git la plus propre et la plus claire est celle qui maintient la branche et valide l'intégrité.
Marnen Laibow-Koser

3
"Mythe commun, vous voyez les commits B et C": Pas nécessairement !! En fait, vous ne voyez B et C que si la fusion était une fusion à avance rapide et cela n'est possible que s'il n'y a pas de conflits. S'il y a des conflits, vous obtenez un seul commit! Cependant: vous pouvez d'abord fusionner le maître dans la fonctionnalité et y résoudre les conflits et si vous fusionnez ensuite la fonctionnalité dans le maître, vous obtenez les validations B et C, et un seul X de validation de la (première) fusion du maître dans la fonctionnalité de votre historique.
Jeremy Benks

très bonne explication! Il doit voter davantage!
dimmits

185

De nombreuses réponses indiquent ici que la fusion transforme toutes vos validations en une seule et suggère donc d'utiliser le rebase pour conserver vos validations. Ceci est une erreur. Et une mauvaise idée si vous avez déjà poussé vos commits .

La fusion n'efface pas vos commits. La fusion préserve l'histoire! (il suffit de regarder gitk) Rebase réécrit l'histoire, ce qui est une mauvaise chose après l'avoir poussé .

Utilisez la fusion - ne pas rebaser chaque fois que vous avez déjà poussé.

Voici le point de vue de Linus (auteur de Git) (maintenant hébergé sur mon propre blog, récupéré par la Wayback Machine ). C'est une très bonne lecture.

Ou vous pouvez lire ma propre version de la même idée ci-dessous.

Baser une branche sur le maître:

  • fournit une idée incorrecte de la façon dont les commits ont été créés
  • pollue le maître avec un tas de commits intermédiaires qui n'ont peut-être pas été bien testés
  • pourrait en fait introduire des ruptures de build sur ces validations intermédiaires en raison des modifications apportées au master entre le moment où la branche de sujet d'origine a été créée et le moment où elle a été rebasée.
  • rend difficile de trouver de bons endroits en maître pour passer à la caisse.
  • Les horodatages des validations ne s'alignent pas sur leur ordre chronologique dans l'arborescence. Ainsi, vous verriez que la validation A précède la validation B dans le maître, mais la validation B a été créée en premier. (Quoi?!)
  • Génère plus de conflits, car les validations individuelles dans la branche du sujet peuvent impliquer chacune des conflits de fusion qui doivent être résolus individuellement (se situant davantage dans l'historique de ce qui s'est produit dans chaque validation).
  • est une réécriture de l'histoire. Si la branche rebasée a été poussée n'importe où (partagée avec quelqu'un d'autre que vous-même), alors vous avez foiré tous les autres qui ont cette branche depuis que vous avez réécrit l'histoire.

En revanche, fusionner une branche de sujet en maître:

  • conserve l'historique de l'endroit où les branches de sujet ont été créées, y compris toutes les fusions du maître à la branche de sujet pour l'aider à rester à jour. Vous avez vraiment une idée précise du code avec lequel le développeur travaillait lors de la construction.
  • master est une branche composée principalement de fusions, et chacune de ces validations de fusion est généralement de `` bons points '' de l'historique qui peuvent être vérifiés en toute sécurité, car c'est là que la branche de sujet était prête à être intégrée.
  • toutes les validations individuelles de la branche de sujet sont conservées, y compris le fait qu'elles se trouvaient dans une branche de sujet, il est donc naturel d'isoler ces modifications et vous pouvez approfondir si nécessaire.
  • les conflits de fusion ne doivent être résolus qu'une seule fois (au moment de la fusion), les modifications de validation intermédiaires apportées dans la branche de rubrique n'ont donc pas à être résolues indépendamment.
  • peut être fait plusieurs fois en douceur. Si vous intégrez périodiquement votre branche de sujet à la maîtrise, les utilisateurs peuvent continuer à s'appuyer sur la branche de sujet et elle peut continuer à être fusionnée indépendamment.

3
De plus, git merge a l'option "--no-ff" (pas d'avance rapide) qui vous permet d'annuler très facilement toutes les modifications introduites par une certaine fusion.
Tiago

3
Juste pour être plus clair: vous vous référez à la situation «chaque fois que vous avez déjà poussé» - cela devrait être en gras. Le lien vers Linus est génial, d'ailleurs, clarifie les choses.
honzajde

2
mais n'est-il pas préférable de «mettre à jour» du maître dans votre branche de sujet, avant de fusionner la branche de sujet en maître via PR (pour résoudre les conflits dans votre branche, pas le maître)? Nous le faisons comme ça, donc la plupart des branches de sujet ont comme dernier commit "fusionner le maître de branche dans le sujet -..." mais ici, cela est répertorié comme une "fonctionnalité" de rebasage et personne ne le mentionne pour la fusion ...?
ProblemsOfSumit

2
@AndrewArnott "La plupart des branches de sujets devraient pouvoir fusionner sans conflits dans leurs branches cibles" Comment cela devrait-il être possible lorsque 20 développeurs travaillent sur 30 branches? Il y aura des fusions pendant que vous travaillez sur la vôtre - alors bien sûr, vous devez mettre à jour votre branche de sujet à partir de la cible avant de créer un PR ... non?
ProblemsOfSumit

3
Pas habituellement, @Sumit. Git peut très bien fusionner l'une ou l'autre direction même si des modifications ont été apportées à l'une ou aux deux branches. Ce n'est que lorsque les mêmes lignes de code (ou très proches) sont modifiées sur deux branches que vous obtiendrez des conflits. Si cela se produit fréquemment dans n'importe quelle équipe, l'équipe devrait repenser la façon dont ils répartissent le travail, car la résolution des conflits est une taxe et les ralentit.
Andrew Arnott

76

TLDR: Cela dépend de ce qui est le plus important - une histoire bien rangée ou une vraie représentation de la séquence de développement

Si un historique bien rangé est le plus important, vous devez d'abord rebaser puis fusionner vos modifications, de sorte qu'il est clair exactement quel est le nouveau code. Si vous avez déjà poussé votre branche, ne rebasez pas à moins que vous ne puissiez en supporter les conséquences.

Si la vraie représentation de la séquence est la plus importante, vous fusionneriez sans rebaser.

Fusionner signifie: créer un seul nouveau commit qui fusionne mes modifications dans la destination. Remarque: Ce nouveau commit aura deux parents: le dernier commit de votre chaîne de commit et le dernier commit de l'autre branche que vous fusionnez.

Rebase signifie: Créez une toute nouvelle série de commits, en utilisant mon ensemble actuel de commits comme conseils. En d'autres termes, calculez à quoi auraient ressemblé mes modifications si j'avais commencé à les faire à partir du moment où je rebasais. Par conséquent, après le rebase, vous devrez peut-être tester à nouveau vos modifications et pendant le rebase, vous pourriez avoir quelques conflits.

Compte tenu de cela, pourquoi voudriez-vous rebaser? Juste pour garder l'historique du développement clair. Supposons que vous travaillez sur la fonctionnalité X et que lorsque vous avez terminé, vous fusionnez vos modifications. La destination aura désormais un seul commit qui dirait quelque chose comme "Ajout de la fonctionnalité X". Maintenant, au lieu de fusionner, si vous rebasiez puis fusionniez, l'historique de développement de destination contiendrait toutes les validations individuelles dans une seule progression logique. Cela rend l'examen des modifications plus tard beaucoup plus facile. Imaginez à quel point vous auriez du mal à revoir l'historique de développement si 50 développeurs fusionnaient constamment diverses fonctionnalités.

Cela dit, si vous avez déjà poussé la branche sur laquelle vous travaillez en amont, vous ne devez pas rebaser, mais fusionner à la place. Pour les branches qui n'ont pas été poussées en amont, rebasez, testez et fusionnez.

Une autre fois que vous voudrez peut-être rebaser, c'est quand vous voulez vous débarrasser des commits de votre branche avant de pousser en amont. Par exemple: les validations qui introduisent tôt du code de débogage et les autres validations ultérieures qui nettoient ce code. La seule façon de le faire est d'exécuter un rebase interactif:git rebase -i <branch/commit/tag>

MISE À JOUR: Vous souhaitez également utiliser le rebase lorsque vous utilisez Git pour vous connecter à un système de contrôle de version qui ne prend pas en charge l'historique non linéaire ( Subversion par exemple). Lorsque vous utilisez le pont git-svn, il est très important que les modifications que vous fusionnez à nouveau dans Subversion soient une liste séquentielle de modifications en plus des modifications les plus récentes dans le tronc. Il n'y a que deux façons de le faire: (1) recréer manuellement les modifications et (2) utiliser la commande rebase, ce qui est beaucoup plus rapide.

MISE À JOUR 2: Une autre façon de penser à un rebase est qu'il permet une sorte de mappage de votre style de développement au style accepté dans le référentiel dans lequel vous vous engagez. Disons que vous aimez vous engager en petits morceaux. Vous avez un commit pour corriger une faute de frappe, un commit pour vous débarrasser du code inutilisé et ainsi de suite. Au moment où vous avez terminé ce que vous devez faire, vous avez une longue série de validations. Supposons maintenant que le référentiel dans lequel vous vous engagez encourage les grands commits, donc pour le travail que vous faites, on peut s'attendre à un ou deux commits. Comment prenez-vous votre chaîne de commits et les compressez à ce qui est attendu? Vous utiliseriez un rebase interactif et écraseriez vos minuscules commits en moins de gros morceaux. La même chose est vraie si l'inverse était nécessaire - si votre style était composé de quelques grands commits, mais le référentiel exigeait de longues chaînes de petits commits. Vous utiliseriez également un rebase pour le faire. Si vous aviez fusionné à la place, vous avez maintenant greffé votre style de validation sur le référentiel principal. S'il y a beaucoup de développeurs, vous pouvez imaginer à quel point il serait difficile de suivre une histoire avec plusieurs styles de commit différents après un certain temps.

MISE À JOUR 3: Does one still need to merge after a successful rebase?Oui, vous le faites. La raison en est qu'un rebase implique essentiellement un "décalage" des commits. Comme je l'ai dit ci-dessus, ces commits sont calculés, mais si vous avez eu 14 commits à partir du point de branchement, alors en supposant que rien ne va mal avec votre rebase, vous aurez 14 commits devant (du point sur lequel vous rebasez) après le rebase est fait. Vous aviez une branche avant un rebase. Vous aurez une branche de la même longueur après. Vous devez toujours fusionner avant de publier vos modifications. En d'autres termes, rebasez autant de fois que vous le souhaitez (encore une fois, seulement si vous n'avez pas poussé vos modifications en amont). Fusionner uniquement après avoir rebasé.


1
Une fusion avec master pourrait entraîner une avance rapide. Dans une branche de fonctionnalité, il peut y avoir des commits, qui ont des bugs mineurs ou ne compilent même pas. Si vous ne faites que des tests unitaires dans une branche de fonctionnalité, certaines erreurs d'intégration peuvent se produire. Avant de fusionner avec master, des tests d'intégration sont requis et peuvent montrer quelques bugs. Si ceux-ci sont corrigés, la fonctionnalité pourrait être intégrée. Comme vous ne souhaitez pas valider le code buggy à maîtriser, un rebase semble nécessaire afin d'empêcher une avance rapide sur tous les commits.
mbx

1
@mbx git mergeprend en charge l' --no-ffoption qui l'oblige à effectuer un commit de fusion.
Gavin S. Yancey

63

Bien que la fusion soit certainement le moyen le plus simple et le plus courant d'intégrer des modifications, ce n'est pas le seul: la refonte est un autre moyen d'intégration.

Comprendre la fusion un peu mieux

Lorsque Git effectue une fusion, il recherche trois commits:

  • (1) Commit ancêtre commun. Si vous suivez l'historique de deux branches dans un projet, elles ont toujours au moins un commit en commun: à ce moment, les deux branches avaient le même contenu et ont évolué différemment.
  • (2) + (3) Points d'extrémité de chaque branche. Le but d'une intégration est de combiner les états actuels de deux branches. Par conséquent, leurs dernières révisions respectives présentent un intérêt particulier. La combinaison de ces trois commits se traduira par l'intégration que nous visons.

Avance rapide ou validation de fusion

Dans des cas très simples, l'une des deux branches n'a pas de nouveau commit depuis que la branchement s'est produite - son dernier commit est toujours l'ancêtre commun.

Entrez la description de l'image ici

Dans ce cas, effectuer l'intégration est très simple: Git peut simplement ajouter toutes les validations de l'autre branche au-dessus de la validation de l'ancêtre commun. Dans Git, cette forme d'intégration la plus simple est appelée fusion "à avance rapide". Les deux branches partagent alors exactement la même histoire.

Entrez la description de l'image ici

Dans de nombreux cas, cependant, les deux branches ont avancé individuellement.

Entrez la description de l'image ici

Pour effectuer une intégration, Git devra créer un nouveau commit qui contient les différences entre eux - le commit de fusion.

Entrez la description de l'image ici

Human Commits & Merge Commits

Normalement, un commit est soigneusement créé par un être humain. C'est une unité significative qui encapsule uniquement les modifications associées et les annote avec un commentaire.

Un commit de fusion est un peu différent: au lieu d'être créé par un développeur, il est créé automatiquement par Git. Et au lieu d'envelopper un ensemble de modifications connexes, son but est de connecter deux branches, tout comme un nœud. Si vous souhaitez comprendre une opération de fusion ultérieurement, vous devez jeter un œil à l'historique des deux branches et au graphique de validation correspondant.

Intégration avec Rebase

Certaines personnes préfèrent se passer de ces validations de fusion automatiques. Au lieu de cela, ils veulent que l'historique du projet semble évoluer en une seule ligne droite. Rien n'indique qu'il ait été divisé en plusieurs branches à un moment donné.

Entrez la description de l'image ici

Passons en revue une opération de rebase étape par étape. Le scénario est le même que dans les exemples précédents: nous voulons intégrer les changements de la branche-B à la branche-A, mais maintenant en utilisant rebase.

Entrez la description de l'image ici

Nous le ferons en trois étapes

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

Tout d'abord, Git "annulera" toutes les validations sur la branche A qui se sont produites après que les lignes ont commencé à se dériver (après la validation de l'ancêtre commun). Cependant, bien sûr, cela ne les rejettera pas: vous pouvez plutôt penser à ces commits comme étant "sauvés temporairement".

Entrez la description de l'image ici

Ensuite, il applique les commits de la branche-B que nous voulons intégrer. À ce stade, les deux branches se ressemblent exactement.

Entrez la description de l'image ici

Dans la dernière étape, les nouveaux validations sur la branche-A sont maintenant réappliquées - mais sur une nouvelle position, en plus des validations intégrées de la branche-B (elles sont basées à nouveau).

Le résultat donne l'impression que le développement s'est produit en ligne droite. Au lieu d'une validation de fusion contenant toutes les modifications combinées, la structure de validation d'origine a été conservée.

Entrez la description de l'image ici

Enfin, vous obtenez une branche A propre, sans commits indésirables et générés automatiquement.

Remarque: Tiré du post génial de git-tower. Les inconvénients de rebaseest également une bonne lecture dans le même article.


+1 pour des diagrammes très sympas. J'ai toujours voulu pouvoir illustrer un exemple de flux git de la même manière sans succès.
Mikayil Abdullayev

60

Avant de fusionner / rebaser:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

Après git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

Après git rebase master:

A <- B <- C <- D' <- E'

(A, B, C, D, E et F sont des commits)

Cet exemple et des informations beaucoup plus bien illustrées sur Git se trouvent dans Git The Basics Tutorial .


30

Cette phrase comprend:

En général, la façon de tirer le meilleur parti des deux mondes est de rebaser les modifications locales que vous avez apportées, mais que vous n'avez pas encore partagées, avant de les pousser afin de nettoyer votre histoire, mais de ne jamais rebaser tout ce que vous avez poussé quelque part .

Source: 3.6 Git Branching - Rebasing, Rebase vs. Merge


25

Cette réponse est largement orientée autour de Git Flow . Les tables ont été générées avec le joli générateur de table ASCII , et les arbres d'historique avec cette merveilleuse commande ( alias as git lg):

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

Les tableaux sont classés par ordre chronologique inverse pour être plus cohérents avec les arbres historiques. Voyez également la différence entre git mergeet git merge --no-fffirst (vous voulez généralement l'utiliser git merge --no-ffcar cela rend votre histoire plus proche de la réalité):

git merge

Commandes:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

Commandes:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge contre git rebase

Premier point: toujours fusionner les fonctionnalités en développement, ne jamais rebaser le développement à partir des fonctionnalités . Ceci est une conséquence de la règle d'or de la refondation :

La règle d'or git rebaseest de ne jamais l'utiliser sur les branches publiques .

En d'autres termes :

Ne jamais rebaser quelque chose que vous avez poussé quelque part.

J'ajouterais personnellement: à moins qu'il ne s'agisse d'une branche de fonctionnalités ET que vous et votre équipe soyez conscients des conséquences .

La question de git mergevs ne git rebases'applique donc presque qu'aux branches de fonctionnalité (dans les exemples suivants, --no-ffa toujours été utilisée lors de la fusion). Notez que comme je ne suis pas sûr qu'il y ait une meilleure solution ( un débat existe ), je ne fournirai que le comportement des deux commandes. Dans mon cas, je préfère utiliser git rebasecar cela produit un arbre d'histoire plus agréable :)

Entre les branches de fonction

git merge

Commandes:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Commandes:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

De developà une branche de fonctionnalité

git merge

Commandes:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git merge --no-ff develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Commandes:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git rebase develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Notes annexes

git cherry-pick

Lorsque vous n'avez besoin que d'un commit spécifique, git cherry-pickc'est une bonne solution (l' -xoption ajoute une ligne qui dit " (choisi à partir de commit ...) " dans le corps du message de commit d'origine, donc c'est généralement une bonne idée de l'utiliser - git log <commit_sha1>pour voir il):

Commandes:

Time           Branch "develop"              Branch "features/foo"                Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git cherry-pick -x <second_commit_sha1>
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Résultat:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

Je ne suis pas sûr de pouvoir l'expliquer mieux que Derek Gourlay ... Fondamentalement, utilisez git pull --rebaseplutôt que git pull:) Ce qui manque cependant dans l'article, c'est que vous pouvez l'activer par défaut :

git config --global pull.rebase true

git rerere

Encore une fois, bien expliqué ici . Mais en termes simples, si vous l'activez, vous n'aurez plus à résoudre le même conflit plusieurs fois.


15

Le livre Pro Git a une très bonne explication sur la page de rebasage .

Fondamentalement, une fusion prendra deux commits et les combinera.

Un rebase ira à l'ancêtre commun sur les deux et appliquera progressivement les modifications les unes sur les autres. Cela donne une histoire «plus propre» et plus linéaire.

Mais lorsque vous rebase, vous abandonnez les validations précédentes et en créez de nouvelles. Vous ne devez donc jamais rebaser un référentiel public. Les autres personnes travaillant sur le référentiel vous détesteront.

Pour cette seule raison, je fusionne presque exclusivement. 99% du temps, mes branches ne diffèrent pas beaucoup, donc s'il y a des conflits, ce n'est qu'à un ou deux endroits.


1
Les fusions ne combinent pas les validations - ce serait réécrire l'histoire. Rebase fait cela.
kellyfj

4

Git rebase est utilisé pour rendre les chemins de branchement dans l'historique plus propres et la structure du référentiel linéaire.

Il est également utilisé pour garder les branches créées par vous privées, car après avoir rebasé et poussé les modifications sur le serveur, si vous supprimez votre branche, il n'y aura aucune preuve de branche sur laquelle vous avez travaillé. Votre succursale est donc votre préoccupation locale.

Après avoir fait le rebase, nous nous débarrassons également d'un commit supplémentaire que nous avons utilisé pour voir si nous faisons une fusion normale.

Et oui, il faut toujours fusionner après un rebase réussi car la commande rebase place simplement votre travail au-dessus de la branche que vous avez mentionnée lors du rebase, par exemple master, et effectue le premier commit de votre branche en tant que descendant direct de la branche master . Cela signifie que nous pouvons maintenant effectuer une fusion rapide pour apporter des modifications de cette branche à la branche principale.


4

Si vous n'êtes qu'un développeur, vous pouvez utiliser le rebasage au lieu de la fusion pour avoir un historique clair

entrez la description de l'image ici


3

Quelques exemples pratiques, quelque peu liés au développement à grande échelle où Gerrit est utilisé pour la révision et l'intégration de la livraison:

Je fusionne lorsque j'élève ma branche de fonctionnalité vers un nouveau maître distant. Cela donne un travail d'élévation minimal et il est facile de suivre l'historique du développement des fonctionnalités dans par exemple gitk .

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

Je fusionne lorsque je prépare un commit de livraison.

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

Je rebase lorsque ma validation de livraison échoue à l'intégration pour une raison quelconque, et je dois la mettre à jour vers un nouveau maître distant.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master

3

Il a été expliqué à plusieurs reprises ce qu'est le rebase et ce qu'est la fusion, mais quand devez-vous utiliser quoi?

Quand faut-il utiliser rebase?

  • quand vous n'avez pas poussé la branche / personne d'autre n'y travaille
  • vous voulez l'histoire complète
  • vous voulez éviter tous les messages de commit "fusionnés .." générés automatiquement

Au fur et à mesure que Git rebase change l'historique. Par conséquent, vous ne devez pas l'utiliser lorsque quelqu'un d'autre travaille sur la même branche / si vous l'avez poussé. Mais si vous avez une branche locale, vous pouvez faire un fusionner le master de rebase avant de fusionner votre branche en master pour garder un historique plus propre. Ce faisant, après avoir fusionné dans la branche principale, il ne sera pas visible que vous avez utilisé une branche dans la branche principale - l'historique est "plus propre" car vous n'avez pas "fusionné .." généré automatiquement, mais vous avez toujours le historique complet dans votre branche principale sans avoir généré automatiquement des validations "fusionnées ..".

Assurez-vous cependant que vous utilisez git merge feature-branch --ff-onlypour vous assurer qu'il n'y a pas de conflits lors de la création d'un seul commit lorsque vous fusionnez votre fonctionnalité à nouveau. Ceci est intéressant si vous utilisez des branches de fonctionnalités pour chaque tâche sur laquelle vous travaillez, car vous obtenez l'historique de la branche de fonctionnalités, mais pas un commit "fusionné .."

Un deuxième scénario serait, si vous vous êtes dérivé d'une branche et que vous voulez savoir ce qui a changé dans la branche principale. Rebase vous donne les informations car elle inclut chaque commit.

Quand devriez-vous utiliser la fusion?

  • lorsque vous avez poussé la branche / d'autres y travaillent aussi
  • vous n'avez pas besoin de l'historique complet
  • simplement fusionner est assez bon pour vous

Lorsque vous n'avez pas besoin ou ne voulez pas avoir tout l'historique d'une branche de fonctionnalité dans votre branche principale ou si d'autres travaillent sur la même branche / vous l'avez poussée. Si vous souhaitez toujours conserver l'historique, fusionnez simplement le masque dans la branche de fonctionnalité avant de fusionner la branche de fonctionnalité dans le master. Cela se traduira par une fusion rapide où vous avez l'historique de la branche de fonctionnalité dans votre maître (y compris la validation de fusion qui était dans votre branche de fonctionnalité parce que vous avez fusionné le maître dans celle-ci).


-4

Quand dois-je l'utiliser git rebase? Presque jamais, car il réécrit l'histoire. git mergeest presque toujours le choix préférable, car il respecte ce qui s'est réellement passé dans votre projet.


1
@benjaminhull Merci! - sauf que j'espère que ma réponse est basée sur des faits. L'opinion à mon humble avis a peu de place dans ce genre de chose: c'est un fait que perdre votre histoire réelle rend la vie plus difficile plus tard.
Marnen Laibow-Koser

1
Se mettre d'accord. La fusion ne conduira jamais à une histoire corrompue, etc. (lorsque vous rebaserez vos commits poussés)
surfrider
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.