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

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

ai-je encore besoin de fusionner après un rebase réussi?

1271
demandé sur Nick Volynkin 2009-04-30 00:26:35

14 réponses

Version Courte

  • Merge prend toutes les modifications dans une branche et les fusionne dans une autre branche dans une propagation.
  • Rebase dit je veux le point sur lequel j'ai bifurqué vers un nouveau point de départ

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

Fusion

  • disons que vous avez créé une branche pour le développement d'une fonctionnalité unique. Quand vous voulez ramener ces changements au maître, vous voulez probablement fusionner (vous ne vous souciez pas de maintenir toutes les propagations intermédiaires).

Rebase

  • un deuxième scénario serait si vous commenciez à faire du développement et puis un autre développeur a fait un changement sans rapport. Vous voulez probablement tirer et puis rebase pour baser vos changements de la version actuelle de repo.
981
répondu Rob Di Marco 2016-07-18 14:23:54

c'est simple, avec rebase vous dites d'utiliser une autre branche comme la 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 que vous l'appelez cool-feature , bien sûr la branche principale est la base de votre nouvelle fonctionnalité.

maintenant, à un certain moment, vous voulez ajouter la nouvelle fonctionnalité que vous avez implémentée dans la branche master . Tu pourrais juste échanger à master et fusionner la branche cool-feature :

$ git checkout master
$ git merge cool-feature

mais de cette façon un nouveau dummy commit est ajouté, si vous voulez éviter spaghetti-histoire vous pouvez rebase :

$ git checkout cool-feature
$ git rebase master

et la fusionner en master :

$ git checkout master
$ git merge cool-feature

cette fois, puisque la branche de sujet a les mêmes commits de maître plus les commits avec la nouvelle fonctionnalité, la fusion sera juste un fast-forward.

298
répondu Aldo 'xoen' Giambelluca 2018-10-05 23:18:57

pour compléter ma propre réponse mentionné par TSamper ,

  • un rebase est assez souvent une bonne idée à faire avant une fusion, parce que l'idée est que vous intégrez dans votre branche Y le travail de la branche B sur laquelle vous allez fusionner.

    Mais encore une fois, avant de fusionner, vous résolvez n'importe quel conflit dans votre branche (c.-à-d.: "rebase", comme dans "la relecture de mon travail dans ma branche à partir d'un récent point de la branche B )

    Si cela est fait correctement, la fusion subséquente de votre branche à la branche B peut être accélérée.

  • un impact de fusion directement la branche de destination B , ce qui signifie que les fusions mieux être trivial, sinon que la branche B peut être long pour revenir à un état stable (temps pour vous 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'occasion de rejouer mon travail d'un point plus récent de B , mais tout en restant dans ma branche.

Dans ce cas, une fusion est toujours nécessaire d'apporter mon "rejoué" travail sur B .

l'autre scénario ( décrit dans Git Ready par exemple), est d'apporter votre travail directement dans B par un rebase (qui conserve tous vos commits nice, ou même vous donner l'occasion de les réordonner par 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:

A arbre git à défaut lorsque nous n'avons pas fusionné ni relocalisée

rebase1

nous obtenons par rebasing:

rebase3

ce second scénario est tout au sujet: Comment puis-je obtenir Nouveau-fonctionnalité de retour dans le maître.

mon point, en décrivant le premier scénario de rebase, est de rappeler à tout le monde qu'un rebase peut aussi être utilisé comme une étape préliminaire à celui (celle d'être "get new-fonction retour en maître").

Vous pouvez utiliser rebase pour introduire d'abord master "dans" la nouvelle branche de fonctionnalité: la rebase rejouera les nouvelles propagations de fonctionnalités à partir du HEAD master , mais toujours dans la nouvelle branche de fonctionnalité, déplaçant efficacement votre point de départ de branche à partir d'un ancien Master commit vers HEAD-master .

Cela vous permet de résoudre tous les conflits dans votre branche (c'est-à-dire, dans l'isolement, 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 à master et fusionner new-feature (ou rebase new-feature sur master si vous voulez préserver les propagations effectuées dans votre branche new-feature ).

:

  • "rebase vs fusion" peut être considéré comme deux manières d'importer une travailler sur, par exemple, master .
  • mais" rebase puis fusionner " peut être un flux de travail valide pour résoudre d'abord le conflit dans l'isolement, puis ramener votre travail.
254
répondu VonC 2017-05-23 12:34:51

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

Fusion pas effacer vos commits. Fusion préserve l'histoire! (il suffit de regarder gitk) Rebase réécrit l'histoire, ce qui est une mauvaise chose après que vous avez poussé il.

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

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

Rebasage d'une branche master:

  • fournit une idée incorrecte de la façon dont les commits ont été créés
  • pollue maître avec un tas d'intermédiaires s'engage à ce que peut ne pas avoir été bien testé
  • pourrait en fait introduire des build breaks sur ces propagations intermédiaires en raison des changements apportés à master entre le moment où la branche thématique originale a été créée et le moment où elle a été rebasée.
  • rend difficile de trouver de bonnes places dans le master.
  • fait que les horodateurs sur les commits ne s'alignent pas avec leur ordre chronologique dans l'arbre. Donc vous verriez que commettre un précède commit B dans master, mais commit b a été écrit en premier. (Quoi?!)
  • produit plus de conflits parce que les commits individuels dans la branche de sujet peuvent chacun impliquer des conflits de fusion qui doivent être résolus individuellement (couchant plus loin dans l'histoire sur ce qui s'est passé dans chaque commit).
  • est une réécriture de l'histoire. Si la branche qui est 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 a que branche depuis que vous avez réécrit l'histoire.

par contraste, la fusion d'une branche de sujet dans le maître:

  • préserve l'histoire de l'endroit où les branches du sujet ont été créées, y compris toute fusion du maître à la branche du sujet pour aider à la garder à jour. Vous avez vraiment une idée précise du code avec lequel le promoteur travaillait quand ils construisaient.
  • master est une branche composée principalement de fusions, et chacun de ces merge commits sont typiquement de "bons points" dans l'histoire qui sont sûrs à vérifier parce que c'est là que la branche de sujet était prête à être intégrée.
  • toutes les propagations individuelles de la branche du sujet sont préservées, y compris le fait qu'elles étaient dans une branche du sujet, donc isoler ces changements est naturel et vous pouvez forer là où nécessaire.
  • les conflits de fusion ne doivent être résolus qu'une seule fois (au moment de la fusion) donc les changements de propagation intermédiaire fait dans la branche de thème ne doivent pas être résolus indépendamment.
  • peut être fait plusieurs fois en douceur. Si vous intégrez votre branche de sujet à maîtriser périodiquement, les gens peuvent continuer à construire sur la branche de sujet et il peut continuer à être fusionné indépendamment.
154
répondu Andrew Arnott 2016-01-22 02:46:30

TL; DR

si vous avez un doute, utilisez merge.

Réponse Courte

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

  • la structure arborescente résultante de l'histoire (généralement seulement perceptible en regardant un graphe de propagation) est différente (l'un aura des branches, l'autre pas).
  • la fusion créera généralement un commit supplémentaire (par exemple un noeud dans le arbre.)
  • fusionner et rebase traitera les conflits différemment. Rebase présentera les conflits un commit à la fois où merge les présentera tous en même temps.

donc la réponse courte est à choisir rebase ou fusionner basé sur ce que vous voulez que votre histoire ressemble à .

Longue Réponse

Il ya quelques facteurs que vous devriez considérer lors du choix de l'opération à utiliser.

est-ce que la branche que vous obtenez des changements est partagée avec d'autres développeurs à l'extérieur de votre équipe (par exemple open source, public)?

si oui, ne rebasez pas. Rebase détruit la branche et ces développeurs auront des dépôts cassés/incohérents sauf s'ils utilisent git pull --rebase . C'est une bonne façon de contrarier rapidement les autres développeurs.

dans quelle mesure votre équipe de développement est-elle compétente?

Rebase est un destructeur opération. Cela signifie que, si vous ne l'appliquez pas correctement, vous pourriez perdre du travail engagé et/ou briser la cohérence des dépôts 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évoué pour faire face à la ramification et la fusion. Ces développeurs ne savent pas grand-chose sur Git et ne veulent pas en savoir beaucoup. Dans ces équipes, Je ne prendrais pas le risque de recommander rebasing pour quelque raison que ce soit.

Fait la direction elle-même représenter de l'information utile

certaines équipes utilisent le Modèle Branche-par-caractéristique où chaque branche représente une caractéristique (ou bugfix, ou sous-caractéristique, etc. Dans ce modèle, la branche aide à identifier des ensembles de propagations connexes. Par exemple, on peut rapidement inverser une fonctionnalité en inversant la fusion de cette branche (pour être honnête, c'est une opération rare). Ou différenciez une caractéristique en comparant deux branches (plus fréquente). Cela permettrait de détruire la branche et ce ne serait pas facile.

j'ai également travaillé sur des équipes qui ont utilisé le Modèle Branche-par-développeur (nous avons tous été là). 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 pas de mal à la relocalisation.

pourriez-vous revenir sur la fusion pour une raison quelconque?

Inverser (comme dans défaire) un rebase est considérablement difficile et/ou impossible (si le rebase avait des conflits) comparé à revenir à une fusion. Si vous pensez qu'il y a une chance que vous voulez revenir en arrière puis utiliser la fusion.

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

Les opérations de rebasage

doivent être effectuées avec un git pull --rebase correspondant . Si vous travaillez seul, vous pourrez peut-être vous rappeler ce que vous devez utiliser au moment opportun. Si vous travaillez sur une équipe, ce 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 --rebase pour toutes les pulls).

Mythes Communs

Fusion détruit de l'histoire (les courges s'engage)

en supposant que vous avez la fusion suivante:

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

certains diront que la fusion "détruit" l'histoire de commit parce que si vous deviez regarder le journal de seulement la branche principale (A-D) vous manquez le important des messages de validation contenu dans B et C.

si c'était vrai, nous n'aurions pas 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 d'essayer pour vous-même.

Rebase permet pour plus de sécurité/de plus simple fusionne

les deux approches fusionnent différemment, mais il n'est pas clair que l'un est toujours meilleur que l'autre et il peut dépendre du workflow du développeur. Par exemple, si un développeur a tendance à s'engager régulièrement (par exemple, s'il s'engage deux fois par jour alors qu'il passe du travail à la maison), il pourrait y avoir beaucoup de commits pour une branche donnée. Bon nombre de ces propagations ne ressemblent pas du tout au produit final (j'ai tendance à reformuler mon approche une ou deux fois par caractéristique). Si quelqu'un d'autre travaillait sur un domaine connexe de code et ils ont essayé de rebaser mes modifications il pourrait être assez fastidieux de l'opération.

Rebase est plus froid / sexy / plus professionnel

si vous aimez alias rm à rm -rf pour" gagner du temps " alors peut-être que rebase est pour vous.

Mes Deux Cents

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. Un peu comme je pense que je vais tomber sur un scénario où git refrog est un génial un outil qui résout mon problème. J'ai travaillé avec git depuis plus de cinq ans maintenant. Il n'est pas arrivé.

Désordre histoires n'ont jamais vraiment été un problème pour moi. Je ne lis jamais mon histoire comme un roman passionnant. La majorité du temps, j'ai besoin d'une histoire, je vais utiliser le blâme git ou git bisect de toute façon. Dans ce cas, avoir la commit merge est en fait utile pour moi parce que si la fusion introduit la question qui est une information significative pour moi.

mise à jour (4/2017)

je me sens obligé de mentionner que j'ai personnellement adouci sur l'utilisation de rebase bien que mon conseil général tient toujours. J'ai récemment beaucoup interagi avec le projet Matériau angulaire 2 . Ils ont utilisé rebase pour garder un historique de propagation très net. Cela m'a permis de voir très facilement ce que commit corrigeait un défaut donné et si oui ou non ce commit était inclus dans une version. Il sert comme un excellent exemple d'utiliser rebase correctement.

124
répondu Pace 2017-05-23 12:18:30

la Fusion signifie: Créer un nouveau commit qui fusionne mes changements dans la destination.

Rebase signifie: créer une nouvelle série entière de commits, en utilisant mon ensemble actuel de commits comme astuces. En d'autres termes, calculez à quoi mes changements auraient ressemblé si j'avais commencé à les faire à partir du point où je vais rebaser. Après le rebase, donc, vous pourriez avoir besoin de tester à nouveau vos changements et pendant le rebase, vous auriez probablement quelques conflits.

étant donné cela, pourquoi vous rebasez? Juste pour garder l'histoire du développement claire. Disons que vous travaillez sur la fonctionnalité X et que lorsque vous avez terminé, vous fusionnez vos modifications. La destination aura désormais une seule propagation qui indiquera quelque chose du genre "ajout de la fonctionnalité X". Maintenant, au lieu de fusionner, si vous rebasé puis fusionné, l'histoire du développement de la destination contiendrait toutes les propagations individuelles dans une seule progression logique. Cela rend l'examen des changements beaucoup plus tard facile. Imaginez à quel point il serait difficile de passer en revue l'histoire du développement Si 50 développeurs fusionnaient diverses fonctionnalités tout le temps.

cela dit, si vous avez déjà poussé la branche sur laquelle vous travaillez en amont, vous ne devriez 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, vous pourriez vouloir rebaser est quand vous voulez vous débarrasser des commits de votre branche avant de pousser en amont. Par exemple: Commits qui introduisent du code de débogage tôt et d'autres commits plus loin qui nettoient ce code. La seule façon de le faire est d'effectuer un rebase interactif: git rebase -i <branch/commit/tag>

mise à jour: vous voulez aussi utiliser rebase lorsque vous utilisez Git pour vous connecter à un système de contrôle de version qui ne supporte pas 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 sont une liste séquentielle des changements en haut des plus récents changements 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, qui est beaucoup plus rapide.

UPDATE2: 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 dépôt dans lequel vous vous engagez. Disons que tu aimes t'engager en petits morceaux. Vous avez un engagement pour corriger une faute de frappe, un s'engage à se débarrasser du code inutilisé et ainsi de suite. Lorsque vous avez terminé ce que vous devez faire, vous avez une longue série de commits. Maintenant, disons que le dépôt dans lequel vous vous engagez encourage les grandes propagations, donc pour le travail que vous faites, on peut s'attendre à une ou peut-être deux propagations. Comment prendre votre chaîne de commits et les comprimer à ce qui est attendu? Vous utiliserez un rebase interactif et écraserez vos minuscules commits en moins de gros morceaux. Le même est vrai si l'inverse était nécessaire - si votre style a été quelques grandes commits, mais le repo a exigé de longues cordes de petites commits. Vous utiliseriez un rebase pour le faire aussi bien. Si vous avez fusionné à la place, vous avez maintenant greffé votre style de propagation sur le dépôt principal. Si il y a beaucoup de développeurs, vous pouvez imaginer combien il serait de suivre une histoire avec plusieurs commettre des styles après un certain temps.

UPDATE3: Does one still need to merge after a successful rebase? Oui, vous le faites. La raison en est que cela implique essentiellement un "décalage" de engager. Comme je l'ai dit plus haut, ces propagations sont calculées, mais si vous aviez 14 propagations du point de ramification, en supposant que rien ne va mal avec votre rebase, vous auriez 14 propagations d'avance (du point sur lequel vous rebasez) après que le rebase soit terminé. 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, rebase autant de fois que vous voulez (encore, seulement si vous n'avez pas poussé vos changements en amont). Fusionnez seulement après avoir rebasé.

65
répondu Carl 2013-08-11 20:38:23

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 and F are commits)""

cet exemple et des informations bien plus illustrées sur git se trouvent ici: http://excess.org/article/2008/07/ogre-git-tutorial /

55
répondu guybrush 2012-06-07 06:19:16

bien que la fusion soit certainement le moyen le plus simple et le plus courant d'intégrer des changements, ce n'est pas le seul: Rebase est un moyen alternatif d'intégration.

comprendre fusionner un peu mieux

quand Git effectue une fusion, il Recherche trois commits:

  • (1) commit D'ancêtre commun si vous suivez l'histoire de deux les directions générales dans un projet, elles ont toujours au moins un engagement en commun: à ce moment-là, les deux directions générales avaient le même contenu et évoluaient ensuite différemment.
  • (2) + (3) Paramètres 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 engage entraînera l'intégration que nous visons.

avance Rapide ou de Fusion s'Engager

dans des cas très simples, l'une des deux branches n'a pas de nouvelle propagation depuis que la ramification s'est produite - sa dernière propagation est toujours l'ancêtre commun.

enter image description here

dans ce cas, effectuer l'intégration est très simple: Git peut simplement ajouter toutes les propagations de l'autre branche sur le dessus du commit ancêtre commun. En Git, cette forme d'intégration la plus simple est appelée une fusion "fast-forward". Les deux branches partagent alors la même histoire.

enter image description here

Dans beaucoup de cas, cependant, les deux branches progressé individuellement. enter image description here

pour faire une intégration, Git devra créer un nouveau commit qui contient les différences entre eux - la fusion s'engager.

enter image description here

L'Homme S'Engage & Fusion S'Engage

Normalement, un commit est soigneusement créé par un être humain. C'est une unité significative qui enveloppe seulement les changements reliés et les annote avec un commentaire.

Une fusion de livraison 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 changements connexes, son but est de connecter deux branches, tout comme un noeud. Si vous voulez comprendre une opération de fusion plus tard, vous devez jeter un coup d'oeil à l'histoire des deux branches et au graphique de propagation correspondant.

intégration avec Rebase

certaines personnes préfèrent aller sans une telle fusion automatique commet. Au lieu de cela, ils veulent que l'histoire du projet ressemble à avait évolué en une seule ligne droite. rien n'indique encore qu'il ait été scindé en plusieurs branches à un moment donné.

enter image description here

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

enter image description here

Nous allons faire cela 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 va "annuler" toutes les propagations sur la branche-A qui se sont produites après que les lignes ont commencé à se déconnecter (après la propagation de l'ancêtre commun). Cependant, bien sûr, il ne les rejettera pas: au lieu de cela, vous pouvez penser que ces commits sont "sauvée Temporairement".

enter image description here

Ensuite, il applique les commits de la branche B que nous voulons intégrer. À ce stade, les deux branches sont identiques.

enter image description here

dans la dernière étape, les nouveaux commits sur la branche-A sont maintenant appliqués-mais sur une nouvelle position, sur le dessus de la intégrée commits de la branche B (ils sont re -). le résultat semble que le développement s'est produit en ligne droite. Au lieu d'une commit de fusion qui contient toutes les modifications combinées, la structure de commit originale a été conservée.

enter image description here

enfin, vous obtenez une branche propre branche-a avec aucune propagation non désirée et générée automatiquement.

Note: , tiré du génial post par git-tower . Le inconvénients de rebase est également une bonne lecture dans le même post.

27
répondu Abdullah Khan 2017-10-12 12:08:52

Cette phrase devient:

En général, la façon d'obtenir le meilleur des deux mondes est à rebase local les changements que vous avez faits mais que vous n'avez pas encore partagés avant de les pousser dans ordre de nettoyer votre histoire, mais ne jamais rebaser quoi que ce soit que vous avez poussé quelque part.

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

25
répondu Joaquin Sargiotto 2015-01-05 13:50:13

Le pro git livre est une très bonne explication sur le rebasage de la page .

fondamentalement une fusion prendra 2 commits et les combinera.

un rebase ira à l'ancêtre commun sur le 2 et d'appliquer progressivement les changements les uns sur les autres. Cela rend l'histoire plus "propre" et plus linéaire.

mais quand vous rebasez vous abandonnez les commits précédents et en créez de nouveaux. Donc tu ne devrais jamais refaites un rapport qui est public. Les autres personnes qui travaillent sur le repo vont te détester.

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, c'est seulement à un ou deux endroits.

15
répondu xero 2013-09-06 07:40:00

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 ASCII , et les arbres d'histoire avec cette commande merveilleuse ( aliased comme 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érent avec les arbres de l'histoire. Voir aussi la différence entre git merge et git merge --no-ff tout d'abord (vous voulez généralement utiliser git merge --no-ff car il rend votre histoire regarder 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 vs git rebase

premier point: toujours fusionner les caractéristiques dans develop, jamais rebase développer à partir de caractéristiques . C'est une conséquence de la règle D'or du rebasement :

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

en d'autres termes :

N'a jamais rebasé quelque chose que vous avez poussé quelque part.

j'ajouterais personnellement: sauf s'il s'agit d'une branche et que vous et votre équipe êtes conscients des conséquences .

ainsi la question de git merge vs git rebase s'applique presque seulement aux branches de caractéristique (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 y ait une meilleure solution ( un débat existe ), je vais seulement fournir comment les deux commandes se comportent. Dans mon cas, je préfère utiliser git rebase car il produit un plus bel arbre de l'histoire :)

entre les branches caractéristiques

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 caractéristique

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

git cherry-pick

quand vous avez juste besoin d'un commit spécifique, git cherry-pick est une bonne solution (l'option -x ajoute une ligne qui dit " " cherry pickled from commit...) " à l'origine le message de validation de corps, de sorte qu'il est généralement une bonne idée de l'utiliser - git log <commit_sha1> pour 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 que je puisse l'expliquer mieux que Derek Gourlay ... Fondamentalement, utiliser git pull --rebase au lieu de git pull :) ce qui manque dans l'article cependant, 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 des termes simples, si vous l'activez, vous n'aurez pas à résoudre le même conflits multiples désormais.

14
répondu sp00m 2017-02-22 14:03:52

git rebase est utilisé pour rendre les chemins de ramification dans l'histoire plus propre et la structure du dépôt 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é. Donc votre branche est maintenant votre préoccupation locale.

après avoir fait rebase nous nous débarrassons aussi d'un commit supplémentaire que nous avions l'habitude de voir si nous normal de fusion.

et oui on a encore besoin de faire la fusion après un rebase réussi car la commande rebase met juste votre travail sur le dessus de la branche que vous avez mentionné pendant le rebase dire Maître et fait le premier commit de votre branche comme un descendant direct de la branche principale. Cela signifie que nous pouvons maintenant faire une fusion rapide en avant pour apporter des changements de cette branche à la branche principale.

4
répondu cvibha 2013-09-24 06:11:26

Quelques exemples concrets, un peu connectés pour le développement à grande échelle où gerrit est utilisé pour la révision et l'intégration de la prestation.

je fusionne quand je monte ma branche principale à un nouveau maître à distance. Cela donne un travail de soulèvement minimal et il est facile de suivre l'histoire du développement de la fonctionnalité 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 quand 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 quand mon delivery commit échoue à l'intégration pour quelque raison que ce soit et je dois le mettre à jour vers un nouveau master distant.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
2
répondu Martin G 2016-02-29 09:51:26

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

-7
répondu Marnen Laibow-Koser 2018-01-19 21:01:38