web-dev-qa-db-fra.com

Quand utilisez-vous git rebase au lieu de git fusion?

Quand est-il recommandé d'utiliser git rebase contre git merge?

Dois-je quand même fusionner après un rebasement réussi?

1334
Coocoo4Cocoa

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 suis branchée 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 fonctionnalité unique. Lorsque vous souhaitez ramener ces modifications au maître, vous souhaitez probablement fusionner (vous ne vous souciez pas de la conservation de tous les validations provisoires). 

Rebase

  • Un deuxième scénario serait que si vous commenciez à faire du développement et qu'un autre développeur effectue un changement indépendant. Vous voudrez probablement tirer puis rebase pour baser vos modifications depuis la version actuelle à partir du référentiel.
1020
Rob Di Marco

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 et que vous créez une branche pour implémenter une nouvelle fonctionnalité, dites-lui que vous l'appelez cool-feature, bien entendu, la branche principale constitue la base de votre nouvelle fonctionnalité.

Désormais, vous souhaitez ajouter la nouvelle fonctionnalité que vous avez implémentée dans la branche master. Vous pouvez simplement passer à master et fusionner la branche cool-feature:

$ git checkout master
$ git merge cool-feature

mais de cette façon, un nouveau commit factice est ajouté. Si vous voulez éviter les spaghettis, vous pouvez rebase:

$ git checkout cool-feature
$ git rebase master

puis fusionnez-le dans master:

$ git checkout master
$ git merge cool-feature

Cette fois, étant donné que la branche de sujet a les mêmes commits de maître ainsi que les commits avec la nouvelle fonctionnalité, la fusion sera simplement une avance rapide.

321

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

  • une refonte est souvent une bonne idée à faire avant une fusion, car l’idée est d’intégrer dans votre branche Y le travail de la branche B sur lequel vous allez fusionner.
    Mais encore une fois, avant de fusionner, vous résolvez tout conflit dans la branche {votre} (c'est-à-dire: "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 suivante de votre branche en branche B peut être effectuée rapidement.

  • un impact de fusion directement sur la branche de destination B, ce qui signifie que les fusions doivent être simples, sinon cette branche B peut être longue pour revenir à un état stable (il est temps de résoudre tous les conflits)


le point de fusion après une rebase? 

Dans le cas que je décris, je rebase B sur ma branche, uniquement pour pouvoir rejouer mon travail à partir d'un point plus récent de B, mais tout en restant dans ma branche.
Dans ce cas, une fusion est encore nécessaire pour que mon travail "rejoué" sur B.

L’autre scénario ( décrit dans Git Ready par exemple) consiste à importer votre travail directement dans B par le biais d’une base (ce qui permet de conserver tous vos commits Nice ou même de vous permettre de les commander à nouveau via une commande interactive. rebase).
Dans ce cas (vous vous rebassez tout en étant dans la branche B), vous avez raison: aucune autre fusion n'est nécessaire:

Un arbre git par défaut quand on n'a pas fusionné ni rebasé

rebase1

nous obtenons en rebasant:

rebase3

Ce deuxième scénario est tout au sujet de: comment puis-je obtenir de nouvelles fonctionnalités dans Master.

Mon but, en décrivant le premier scénario de rebase, est de rappeler à tout le monde qu’un rebase peut également être utilisé comme étape préliminaire à cela (c’est-à-dire "ramener la nouvelle fonctionnalité dans le maître").
Vous pouvez utiliser rebase pour amener d'abord le maître "dans" la nouvelle branche: la nouvelle base relit les commits de nouvelle fonctionnalité à partir du HEAD master, mais toujours dans la nouvelle branche, ce qui déplace effectivement le point de départ de votre branche d'un ancien master commit to HEAD-master.
Cela vous permet de résoudre tout conflit dans votre branche (ce qui signifie, isolément, tout en permettant à maître de continuer à évoluer en parallèle si l'étape de résolution du conflit prend trop de temps).
Ensuite, vous pouvez passer à master et fusionner new-feature (ou redéfinir new-feature sur master si vous souhaitez conserver les validations effectuées dans votre branche new-feature).

Alors: 

  • "rebase vs. merge" peut être considéré comme deux manières d'importer un travail sur, disons, master
  • Mais "rebase then fusionner" peut être un flux de travail valide pour résoudre d'abord un conflit de manière isolée, puis ramener votre travail.
259
VonC

Beaucoup de réponses ici disent que la fusion transforme tous vos commits en un, et suggèrent donc d'utiliser rebase pour préserver vos commits. Ceci est une erreur. Et une mauvaise idée si vous avez déjà poussé vos commits.

La fusion fait pas effacer vos commits. Fusionner préserve l'histoire! (il suffit de regarder gitk) Rebase réécrit l’histoire, ce qui est une mauvaise chose après que vous l’ayez poussée.

Use merge - ne rebase pas chaque fois que vous avez déjà poussé.

Voici Linus (auteur de git) prend sur elle . C'est une très bonne lecture. Ou vous pouvez lire ma propre version de la même idée ci-dessous.

Redéfinition d’une branche sur master:

  • 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 réellement introduire des coupures de construction sur ces commits intermédiaires en raison des modifications apportées à la gestion du moment où la branche de sujet d'origine a été créée et de son rebasement.
  • il est difficile de trouver de bons endroits en master à la caisse.
  • Les horodatages des commits ne sont pas alignés sur leur ordre chronologique dans l'arborescence. Ainsi, vous verriez que le commit A précède le commit B dans le maître, mais que le commit B a été créé en premier. (Quoi?!)
  • Génère plus de conflits parce que les commits individuels dans la branche thématique peuvent impliquer des conflits de fusion qui doivent être résolus individuellement (L'historique de ce qui s'est passé dans chaque commit est plus détaillé.).
  • est une réécriture de l'histoire. Si la branche en cours de rebasement a été poussée n'importe où (partagée avec une personne autre que vous-même), alors vous avez foiré tout le monde qui possède cette branche depuis que vous avez réécrit l'historique. 

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

  • conserve l'historique des endroits où les branches de sujet ont été créées, y compris les fusions de la branche principale à la branche de sujet, afin de rester à jour. Vous avez vraiment une idée précise du code avec lequel le développeur travaillait au moment de la construction.
  • master est une branche composée principalement de fusions, et chacun de ces commits de fusion est généralement un "bon point" de l'histoire qu'il est prudent de vérifier, car c'est là que la branche de sujet était prête à être intégrée.
  • tous les commits individuels de la branche de sujet sont conservés, y compris le fait qu'ils se trouvaient dans une branche de sujet; par conséquent, il est naturel d'isoler ces modifications et vous pouvez effectuer une exploration, si nécessaire.
  • les conflits de fusion ne doivent être résolus qu'une seule fois (au moment de la fusion), de sorte que les modifications de validation intermédiaires effectuées dans la branche de sujet ne doivent pas être résolues indépendamment.
  • peut être fait plusieurs fois en douceur. Si vous intégrez votre branche de sujet à maîtriser périodiquement, les utilisateurs peuvent continuer à construire sur la branche de sujet et celle-ci peut continuer à être fusionnée indépendamment.
163
Andrew Arnott

TL; DR

Si vous avez un doute, utilisez la fusion.

Réponse courte

Les seules différences entre une base et une fusion sont les suivantes:

  • L'arborescence résultante de l'historique (généralement visible uniquement lorsque vous regardez un graphique de validation) est différente (l'une aura des branches, l'autre pas).
  • La fusion créera généralement une validation supplémentaire (par exemple, un nœud dans l’arborescence).
  • Fusionner et rebaser gérera les conflits différemment. Rebase présentera les conflits un commet à un moment où la fusion les présentera tous en même temps.

La réponse courte est donc de choisir une base ou une fusion en fonction de ce que vous voulez que votre historique ressemble à.

Longue réponse

Il y a quelques facteurs à prendre en compte lors du choix de l'opération à utiliser.

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

Si c'est le cas, ne rebase pas. Rebase détruit la branche et ces développeurs auront des référentiels endommagés/incohérents à moins qu’ils utilisent git pull --rebase. C'est un bon moyen de contrarier rapidement les autres développeurs.

Quel est le niveau de compétence de votre équipe de développement?

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

J'ai travaillé au sein d'équipes où les développeurs venaient tous d'une époque où les entreprises pouvaient se permettre un personnel dédié pour gérer les branches et les fusions. Ces développeurs ne connaissent pas beaucoup Git et ne veulent pas en savoir plus. Dans ces équipes, je ne risquerais pas de recommander un changement de base pour une raison quelconque.

La branche représente-t-elle des informations utiles?

Certaines équipes utilisent le modèle branche par entité, chaque branche représentant une fonctionnalité (ou une correction de bogue, ou une sous-fonctionnalité, etc.). Dans ce modèle, la branche aide à identifier des ensembles de validations associées. Par exemple, on peut rapidement revenir sur une fonctionnalité en annulant la fusion de cette branche (pour être honnête, il s'agit d'une opération rare). Ou diff une fonctionnalité en comparant deux branches (plus communes). Rebase détruirait la branche et ce ne serait pas simple.

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

Pourriez-vous vouloir annuler la fusion pour une raison quelconque?

L'annulation (comme l'annulation) d'une base est considérablement difficile et/ou impossible (si la base a des conflits) par rapport à l'annulation d'une fusion. Si vous pensez qu'il est possible que vous souhaitiez revenir en arrière, utilisez la fusion.

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

Les opérations de base doivent être extraites avec un git pull --rebase correspondant. Si vous travaillez seul, vous pourrez peut-être vous rappeler lequel vous devriez utiliser au moment opportun. Si vous travaillez en équipe, ce sera très difficile à coordonner. C'est la raison pour laquelle la plupart des flux de travaux de refonte recommandent d'utiliser rebase pour toutes les fusions (et git pull --rebase pour toutes les analyses).

Mythes communs

La fusion détruit l'histoire (squash commits)

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 consultiez que le journal de la branche principale (A - D), vous perdriez les messages de validation importants contenus dans B et C.

Si cela était vrai, nous n'aurions pas des questions comme celle-ci . En gros, vous verrez B et C sauf si vous demandez explicitement de ne pas les voir (en utilisant --first-parent). C'est très facile d'essayer vous-même.

Rebase permet des fusions plus sûres/plus simples

Les deux approches fusionnent différemment, mais il n'est pas évident que l'une soit 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, peut-être qu’il s’engage deux fois par jour lorsqu’il passe du travail au domicile), il peut y avoir beaucoup d’engagements pour une branche donnée. Beaucoup de ces commits pourraient 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 tentait de modifier ses modifications, l'opération pourrait s'avérer fastidieuse.

Rebase est plus cool/plus sexy/plus professionnel

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

Mes deux centimes

Je pense toujours qu'un jour je rencontrerai un scénario dans lequel git Rebase est l'outil génial qui résout le problème. Un peu 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. Cela n'est pas arrivé.

Les histoires désordonnées n'ont jamais é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 de toute façon utiliser le blâme git ou le couperet. Dans ce cas, la validation de fusion m’est utile, car si la fusion 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 en utilisant rebase bien que mes conseils généraux tiennent toujours. J'ai récemment eu beaucoup d'interactions avec// Angular 2 Material -/project. Ils ont utilisé rebase pour garder une histoire 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. C'est un excellent exemple d'utilisation correcte de Rebase.

I feel obligated to mention that I have personally softened on using rebase although my general advice still stands. I have recently been interacting a lot with the Angular 2 Material project. They have used rebase to keep a very clean commit history. This has allowed me to very easily see what commit fixed a given defect and whether or not that commit was included in a release. It serves as a great example of using rebase correctly.

146
Pace

Fusionner signifie: créer un nouveau commit qui fusionne mes modifications dans la destination.

Rebase signifie: Créez une nouvelle série de commits, en utilisant mes ensembles de commits actuels comme indications. En d’autres termes, calculez à quoi mes modifications auraient ressemblé si j’avais commencé à les modifier à partir du point sur lequel je me basais. Après la création de la base, vous devrez peut-être tester à nouveau vos modifications et, au cours de la création de la base, vous aurez éventuellement 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 comportera désormais un seul commit qui indiquerait quelque chose du type "Ajoutée fonctionnalité X". Désormais, au lieu de fusionner, si vous refaites puis fusionniez, l'historique de développement de la destination contiendrait tous les commits individuels dans une progression logique unique. Cela facilite la révision ultérieure des modifications. Imaginez à quel point il vous serait difficile de passer en revue l’historique du développement si 50 développeurs fusionnaient différentes fonctionnalités à tout moment. 

Cela dit, si vous avez déjà poussé la branche sur laquelle vous travaillez en amont, vous ne devez pas rebaser mais plutôt fusionner. Pour les branches qui n'ont pas été poussées en amont, rebassez, 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 du code de débogage à un stade précoce et les autres validations qui nettoient ce code. La seule façon de faire est d’effectuer une rebase interactive: git rebase -i <branch/commit/tag>

UPDATE: Vous souhaitez également utiliser 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 dans Subversion constituent une liste séquentielle des modifications apportées en plus des modifications les plus récentes apportées au 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.

UPDATE2: Une autre façon de penser à une base est de permettre une sorte de correspondance entre votre style de développement et le style accepté dans le référentiel auquel vous vous engagez. Disons que vous aimez vous engager par petits morceaux. Vous avez un engagement pour corriger une faute de frappe, un engagement pour supprimer le code inutilisé, etc. Lorsque vous avez terminé ce que vous devez faire, vous avez une longue série de commits. Supposons maintenant que le référentiel auquel vous vous engagez encourage les gros commits. Par conséquent, pour le travail que vous faites, on pourrait s’attendre à un ou peut-être deux commits. Comment prenez-vous votre chaîne de commits et les compressez-vous à ce qui est attendu? Vous utiliseriez une base interactive et réduiriez vos petits 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 que le repo 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 combien il serait difficile de suivre une histoire avec plusieurs styles de commit différents après un certain temps.

UPDATE3: Does one still need to merge after a successful rebase? Oui, c'est votre cas. La raison est qu'un rebase implique essentiellement un "déplacement" des commits. Comme je l’ai dit plus haut, ces commits sont calculés, mais si vous avez 14 commissions à partir du point de ramification, en supposant que rien ne se passe mal avec votre base, vous aurez 14 commissions devant (du point sur lequel vous vous refaites) après la rebase est faite. Vous avez eu une branche avant un rebase. Vous aurez ensuite une branche de même longueur. Vous devez encore fusionner avant de publier vos modifications. En d'autres termes, rebassez autant de fois que vous le souhaitez (à nouveau, uniquement si vous n'avez pas poussé vos modifications en amont). Fusionner uniquement après avoir rebasé.

66
Carl

avant fusion/rebase:

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 illustrées sur git se trouvent ici: http://excess.org/article/2008/07/ogre-git-tutorial/

57
guybrush

Bien que la fusion soit le moyen le plus simple et le plus courant d’intégrer les changements, ce n’est pas le seul: Rebase est un moyen alternatif d’intégration.

Comprendre un peu mieux fusionner

Lorsque Git effectue une fusion, il recherche trois commits:

  • (1) Commit ancêtre commun Si vous suivez l'historique de deux branches d'un projet, elles ont toujours au moins une validation commune: à ce moment-là, les deux branches avaient le même contenu et évoluaient ensuite différemment.
  • (2) + (3) Points de terminaison de chaque branche L'objectif 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 aboutira à l'intégration que nous visons.

Validation rapide ou fusion

Dans des cas très simples, l'une des deux branches n'a pas de nouveau commit depuis la création de la branche. Son dernier commit est toujours l'ancêtre commun.

 enter image description here

Dans ce cas, effectuer l'intégration est extrêmement simple: Git peut simplement ajouter tous les commits de l'autre branche au-dessus du commit de l'ancêtre commun. Dans Git, cette forme d'intégration la plus simple s'appelle une fusion "Fast-Forward". Les deux branches partagent alors exactement la même histoire.

 enter image description here

Cependant, dans de nombreux cas, les deux branches ont progressé individuellement .  enter image description here

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

 enter image description here

Human Commits & Merge Commits

Normalement, un commit est soigneusement créé par un être humain. C'est une unité significative qui résume 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 objectif est de connecter deux branches, comme un nœud. Si vous souhaitez comprendre une opération de fusion ultérieurement, vous devez consulter l'historique des deux branches et le graphe de validation correspondant.

Intégration avec Rebase

_ {Certaines personnes préfèrent se passer de telles commandes de fusion automatiques. Au lieu de cela, elles souhaitent que l'historique du projet semble avoir évolué en une seule ligne droite.quelque point.

 enter image description here

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

 enter image description here

Nous allons le faire en trois étapes

  1. git rebase branch-A // syncs 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" tous les commits sur la branche A qui se sont produits après que les lignes aient commencé à se ramifier (après la validation de l'ancêtre commun). Cependant, bien sûr, il ne les écartera pas: vous pouvez plutôt penser à ces commits comme étant "sauvés temporairement".

 enter image description here

Ensuite, il applique les commits de la branche B que nous souhaitons intégrer. À ce stade, les deux branches ont exactement la même apparence.

 enter image description here

Dans la dernière étape, les nouveaux commits sur la branche A sont maintenant réappliqués - mais sur un nouveau poste, en plus des commits intégrés de la branche B (ils sont ré-basés) .Le résultat ressemble à un développement était arrivé en ligne droite. Au lieu d'une validation de fusion contenant toutes les modifications combinées, la structure de validation d'origine a été préservée.

 enter image description here

Enfin, vous obtenez une branche propre branch-A sans commits non désirés et générés automatiquement.

Note: Tiré de l’impressionnant post de git-tower . Le inconvénients} de rebase est également une bonne lecture dans le même message.

40
Abdullah Khan

Cette phrase comprend:

En général, le meilleur moyen d’obtenir le meilleur des deux mondes est de rebaser local les modifications que vous avez apportées mais que vous n'avez pas encore partagées avant de les envoyer nettoyer votre histoire, mais ne refaites jamais rien de ce que vous avez poussé quelque part.

Source: http://www.git-scm.com/book/en/v2/Git-Branching-Rebasing#Rebase-vs.-Merge

26
Joaquin Sargiotto

Le livre pro git est une très bonne explication sur la page rebasing .

Fondamentalement, une fusion prendra 2 commits et les combinera.

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

Mais lorsque vous rebasez, vous abandonnez les commits précédents et créez-en de nouveaux. Donc, vous ne devriez jamais rebasonner un dépôt qui est public. Les autres personnes travaillant sur le repo vous haïront.

Pour cette seule raison, je fusionne presque exclusivement. 99% du temps, mes succursales ne diffèrent pas beaucoup, alors s’il ya des conflits, ce n’est qu’à un ou deux endroits.

15
xero

Cette réponse est largement orientée autour de Git Flow . Les tables ont été générées avec le générateur de table Nice ASCII , et les arbres de l'historique avec cette commande merveilleuse ( aliasé en tant que 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 dans l'ordre chronologique inverse pour être plus cohérents avec les arbres de l'historique. Voyez également la différence entre git merge et git merge --no-ff en premier (vous voulez généralement utiliser git merge --no-ff car cela rapproche votre historique 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 vs git rebase

Premier point: toujours fusionner les fonctionnalités pour développer, ne jamais rebase développer à partir de fonctionnalités. Ceci est une conséquence de la règle d'or du changement de base :

La règle d'or de git rebase est de ne jamais l'utiliser sur public branches.

En d'autres termes :

Ne rebase jamais ce que tu as poussé quelque part.

Personnellement, j’ajouterais: sauf s’il s’agit d’une branche technique ET que votre équipe est consciente des conséquences}.

Ainsi, la question de git merge vs git rebase s'applique presque uniquement aux branches de fonctionnalités (dans les exemples suivants, --no-ff a toujours été utilisé lors de la fusion). Notez que, comme je ne suis pas sûr qu'il existe 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 rebase car cela produit un arbre historique plus agréable :)

Entre branches

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 development
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 development
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 de côté

git cherry-pick

Lorsque vous avez juste besoin d’un commit spécifique, git cherry-pick est une solution intéressante (l’option -x ajoute une ligne indiquant "(cherry tirée de commit ...)}" dans le corps du message de commit initial; bonne idée de l’utiliser - git log <commit_sha1> pour le voir):

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

Pas sûr de pouvoir l'expliquer mieux que Derek Gourlay ... En gros, utilisez git pull --rebase au lieu de git pull :) Ce qui manque dans cet article, c'est que vous pouvez l'activer par défaut :

git config --global pull.rebase true

git rerere

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

15
sp00m

Git rebase est utilisé pour rendre les chemins de branchement dans le nettoyeur d'historique et la structure de référentiel linéaires. 

Il est également utilisé pour garder les branches créées par vous privées, car après la modification de la base et le transfert des modifications au serveur, si vous supprimez votre branche, il n'y aura aucune preuve de la branche sur laquelle vous avez travaillé. Donc, votre succursale est maintenant votre préoccupation locale.

Après avoir fait rebase, nous nous débarrassons également d'un commit supplémentaire que nous avions l'habitude de voir si nous réalisions une fusion normale. 

Et oui, il faut encore fusionner après une commande réussie, car la commande rebase place simplement votre travail au-dessus de la branche que vous avez mentionnée lors de la refonte dire 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 transférer les modifications de cette branche vers la branche principale.

4
cvibha

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és vers un nouveau maître distant. Cela donne un travail de soulèvement minimal et il est facile de suivre l'historique du développement des fonctionnalités, par exemple dans 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 me rebase lorsque l'intégration de la validation de livraison échoue 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
2
Martin G

Regardons un exemple. Tout en travaillant sur une branche nommée login, sur la base de la branche master, l'un des membres de votre équipe a appliqué certaines modifications à master. Vous avez besoin de ces modifications pour terminer la fonction login de votre branche.

enter image description here Figure 1. Les nouveaux commits dans la branche master (E et F) sont nécessaires pour terminer le travail dans la branche login.

La fusion de la branche master dans la vôtre aboutirait à une validation de fusion, qui comprend les modifications entre les deux branches et existe pour indiquer où une fusion est effectuée. s'est produite.

enter image description here Figure 2. La fusion des deux branches donne lieu à un commit de fusion.

Nous n’aurons pas besoin de savoir quand nous avons fusionné la master dans la branche login à l’avenir. Au lieu de cela, nous voudrions prétendre que tous les commits sur la branche login se sont produits en fonction du nouvel état de la branche master.

La commande Git rebase rembobine temporairement les commits sur votre branche actuelle, les valide depuis l'autre branche et réapplique les commits rembobinés. En commutant le courant Ceci base la branche en cours sur l'autre branche.

enter image description here

Figure 3. Le changement de base applique les commits de la branche login au-dessus de la branche master.

La source est ici

0
yoAlex5

On a expliqué à maintes reprises ce qu'est le rebase et la fusion, mais quand utiliser quoi?

Quand utiliser rebase?

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

Comme git rebase change l'histoire. Par conséquent, vous ne devriez pas l'utiliser quand quelqu'un d'autre travaille sur la même branche/si vous l'avez poussé. Toutefois, si vous avez une branche locale, vous pouvez créer un maître de base de fusion avant de fusionner votre branche en maître afin de conserver un historique plus propre. Ainsi, après la fusion dans la branche principale, il ne sera pas visible que vous ayez utilisé une branche dans la branche principale. L’historique est "plus propre", car vous n’avez pas automatiquement généré historique dans votre branche principale sans avoir généré automatiquement les modifications "fusionnées ..". Assurez-vous cependant que vous utilisez git merge feature-branch --ff-only pour vous assurer qu'il n'y a pas de conflit créant un seul commit lorsque vous fusionnez votre fonctionnalité sur main.

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

Quand utiliser la fusion?

  • quand vous avez poussé la branche/d'autres personnes 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 souhaitez pas disposer de l'historique d'une branche de fonctionnalités dans votre branche principale ou si d'autres personnes travaillent sur la même branche/vous l'avez poussée. Si vous souhaitez toujours avoir l'historique, fusionnez simplement le maître dans la branche de fonctionnalité avant de fusionner la branche de fonctionnalité dans le maître. Cela entraînera une fusion rapide dans laquelle vous aurez l'historique de la branche de fonctions dans votre maître (y compris la validation de fusion qui était dans votre branche de fonctions car vous y avez fusionné le maître).

0
Jeremy Benks