982 votes

de git et rebase vs fusion questions

J'ai été en utilisant git depuis quelques mois sur un projet avec un autre développeur. J'ai plusieurs années d'expérience avec svn, donc je suppose que je vous apporte un lot de bagages à la relation.

J'ai entendu dire que git est excellent pour le branchement et la fusion, et jusqu'à présent, je n'ai juste pas le voir. Bien sûr, la ramification est mort simple, mais quand j'essaie de fusionner, tout va en enfer. Maintenant, j'y suis habitué depuis le svn, mais il me semble que j'ai juste échangé l'un sous la normale système de gestion de versions pour un autre.

Mon partenaire me dit que mes problèmes viennent de mon désir de fusion de bon gré mal gré, et que je devrais être à l'aide de rebase au lieu de fusionner dans de nombreuses situations. Pour exemple, voici le flux de travail qu'il s'est fixée:

cloner le repo distant
git checkout -b my_new_feature
..de travail et de commettre quelques trucs
git rebase maître
..de travail et de commettre quelques trucs
git rebase maître
..la finition de la fonction
git checkout master
git merge my_new_feature

Essentiellement, créer une branche, TOUJOURS rebase de maître à la direction générale, et de la fusion de la branche master. Important à noter est que la branche reste toujours local.

Ici, c'est le flux de travail que j'ai commencé avec

clone distance repo
créer my_new_feature direction sur la télécommande repo
git checkout -b --piste my_new_feature origine/my_new_feature
..de travail, de valider, de les pousser à l'origine/my_new_feature
git merge master (pour obtenir les quelques modifications que mon partenaire ajouté)
..de travail, de valider, de les pousser à l'origine/my_new_feature
git merge master
..finition my_new_feature, pousser à l'origine/my_new_feature
git checkout master
git merge my_new_feature
supprimer la branche distante
supprimer la succursale locale

Il y a 2 différences essentielles (je pense): j'utilise de la fusion de toujours au lieu de relocalisation, et je pousse ma branche (et ma branche s'engage) à la distance de prise en pension.

Mon raisonnement pour la branche distante, c'est que je veux que mes travaillé sauvegardés comme je suis en train de travailler. Notre repo est automatiquement sauvegardé et peut être restaurée si quelque chose va mal. Mon portable n'est pas, ou pas complètement. Donc, j'ai hate d'avoir le code sur mon ordinateur portable qui n'est pas en miroir à un autre endroit.

Mon raisonnement pour la fusion au lieu de rebase est que l'opération de fusion semble être la norme, et cela semble être une fonctionnalité avancée. Mon sentiment est que ce que je suis en train de faire n'est pas une configuration plus avancée, de sorte que cela ne devrait pas être nécessaire. J'ai même pas lu la nouvelle Pragmatique de Programmation livre sur git, et ils couvrent de fusion de manière approfondie et à peine mention de rebase.

De toute façon, je suivais mon flux de travail sur un récent de la branche, et quand j'ai essayé de l'intégrer à maîtriser, tout est allé en enfer. Il y avait des tonnes de conflits avec des choses qui ne devrait pas avoir d'importance. Les conflits juste fait aucun sens pour moi. Il m'a fallu une journée pour tout régler, et finalement abouti à un obligé de pousser le maître, depuis que mon maître a tous les conflits résolus, mais la distance n'était toujours pas satisfait.

Qu'est-ce que la "bonne" flux de travail pour quelque chose comme ça? Git est censée faire le branchement et la fusion de super-facile, et je ne suis pas le voir.

Mise à jour 2011-04-15

Cela semble être un très populaire en question, alors j'ai pensé que je mettrais à jour avec mes 2 années d'expérience puisque j'ai d'abord demandé.

Il s'avère que l'origine de flux de travail est correcte, au moins dans notre cas. En d'autres termes, c'est ce que nous faisons et ça fonctionne:

cloner le repo distant
git checkout -b my_new_feature
..de travail et de commettre quelques trucs
git rebase maître
..de travail et de commettre quelques trucs
git rebase maître
..la finition de la fonction
git checkout master
git merge my_new_feature

En fait, notre flux de travail est un peu différent, comme nous avons tendance à faire de squash fusionne au lieu de matières premières fusions. Cela nous permet de transformer l'ensemble de notre branche en un seul commit sur le master. Puis nous supprimer notre branche. Cela nous permet logiquement de la structure de notre engage sur le maître, même s'ils sont un peu en désordre sur l'une de nos succursales. Donc, c'est ce que nous faisons:

cloner le repo distant
git checkout -b my_new_feature
..de travail et de commettre quelques trucs
git rebase maître
..de travail et de commettre quelques trucs
git rebase maître
..la finition de la fonction
git checkout master
git merge --squash my_new_feature
git commit -m "ajout my_new_feature"
git branch-D my_new_feature

J'ai appris à aimer git et ne veux plus jamais retourner à SVN. Si vous avez de la peine, il suffit de coller avec elle et à la fin vous allez voir la lumière au bout du tunnel.

390voto

nilbus Points 5476

TL;DR

Un git rebase flux de travail ne vous protège pas des gens qui sont mauvais pour la résolution des conflits ou des gens qui sont habitués à un SVN flux de travail, comme suggéré en Évitant Git Catastrophes: Une Histoire Sanglante. Il ne fait que de la résolution des conflits plus pénible pour eux et les rend plus difficile de récupérer de la mauvaise résolution des conflits. Au lieu de cela, utiliser diff3 de sorte qu'il n'est pas si difficile, en premier lieu.


Rebase flux de travail n'est pas mieux pour la résolution des conflits!

Je suis très pro-rebase pour le nettoyage de l'histoire. Cependant, si j'ai jamais touché à un conflit, j'ai immédiatement interrompre le rebase et de faire une fusion de la place! - Il vraiment me tue que les gens sont de recommander un rebase flux de travail comme une meilleure alternative à une opération de fusion de flux de travail pour la résolution des conflits (ce qui est exactement ce que cette question a été de savoir).

Si elle va de "tous à l'enfer" lors d'une fusion, ça va aller "tous en enfer", lors d'un rebase, et potentiellement beaucoup plus d'enfer aussi! Voici pourquoi:

Raison n ° 1: Résoudre les conflits une fois, au lieu d'une seule fois pour chaque commit

Lorsque vous rebase au lieu de fusionner, vous devrez procéder à la résolution des conflits à autant de fois que vous avez s'engage à rebase, pour le même conflit!

Vrai scénario

Je branche hors de maître de refactoriser une méthode compliquée dans une branche. Mon refactoring de travail est composé de 15 commet total que je travaille à refactoriser le code et obtenir des revues de code. Une partie de mon refactoring consiste à fixer le mixte des tabulations et des espaces qui étaient présents en maître avant que. C'est nécessaire, mais malheureusement, il sera en conflit avec tous les changements apportés par la suite à cette méthode dans le maître. Bien sûr, alors que je travaille sur cette méthode, quelqu'un fait simple, un changement légitime à la même méthode dans la branche master qui devraient être fusionnées avec mes modifications.

Quand il est temps de fusionner ma branche master, j'ai deux options:

git merge: Je reçois un conflit. Je vois le changement qu'ils ont fait pour maîtriser et de le fusionner avec le produit final de) ma branche. Fait.

git rebase: Je reçois un conflit avec mon premier commit. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon deuxième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon troisième s'engager. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon quatrième s'engager. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon cinquième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon sixième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon septième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon huitième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon neuvième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon dixième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon onzième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon douzième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon treizième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon quatorzième commettre. - Je résoudre le conflit et de continuer le rebase. Je reçois un conflit avec mon quinzième commettre. - Je résoudre le conflit et de continuer le rebase.

Vous avez obtenu d'être plaisante-moi si cela est de votre flux de travail préféré. Tout ce qu'il faut est un espace correctif qui est en conflit avec un changement effectué sur le maître, et chaque livraison sera en conflit et doit être résolu. Et c'est un simple scénario avec seulement un espace de conflit. À dieu ne plaise, vous avez un réel conflit impliquant les principaux changements de code dans des fichiers et se résoudre qu' à plusieurs reprises.

Avec tous les extras de la résolution des conflits que vous devez faire, c'est juste augmente la possibilité que vous faites une erreur. Mais les erreurs sont bien dans git, puisque vous pouvez l'annuler, à droite? Sauf, bien sûr...

Raison n ° 2: Avec cela, il n'y a pas d'annulation!

Je pense que nous pouvons tous convenir que la résolution des conflits peut être difficile, et aussi que certaines personnes sont très mauvais. Il peut être très sujettes à des erreurs, pourquoi c'est si grande que git le rend facile à enlever!

Lors de la fusion d'une branche git crée une fusion s'engager à ce que peut être supprimé ou modifié si la résolution des conflits va mal. Même si vous avez déjà poussé la mauvaise fusion s'engager pour le public, faisant autorité repo, vous pouvez utiliser git revert annuler les modifications introduites par la fusion et de refaire la fusion correctement dans une nouvelle fusion de la validation.

Lorsque vous rebase une branche, dans le cas probable où la résolution des conflits est mal fait, vous êtes foutu. Chaque livraison contient maintenant la mauvaise fusion, et vous ne pouvez pas simplement refaire le rebase*. Au mieux, vous devez revenir en arrière et modifier chacun de ces validations. Pas de plaisir.

Après un rebase, il est impossible de déterminer ce qui était à l'origine une partie de la commet et ce qui a été présenté comme un résultat d'une mauvaise résolution des conflits.

*Il est possible d'annuler un rebase si vous creusez la vieille refs de git de journaux internes, ou si vous créez une troisième branche qui pointe à la dernière validation avant le changement d'année de base. Bien entendu, vous devez garder cette branche autour jusqu'à ce que vous avez été convaincu que les conflits ont été résolus correctement sans introduire des bogues. Mais remarquez que personne n'en avait que dans le cadre de leur rebase flux de travail...

Prendre l'enfer hors de résolution des conflits: l'utilisation diff3

Prendre ce conflit par exemple:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

En regardant le conflit, il est impossible de dire ce que chaque branche a changé ou que son intention était. C'est la principale raison, à mon avis, pourquoi la résolution des conflits est confuse et difficile.

diff3 à la rescousse!

git config --global merge.conflictstyle diff3

Lorsque vous utilisez le diff3, chaque nouveau conflit 3e section, la fusion ancêtre commun.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Examinons tout d'abord la fusion de leur ancêtre commun. Puis comparer chaque côté afin de déterminer chaque branche de l'intention. Vous pouvez voir que la TÊTE a changé EmailMessage à TextMessage. Son intention est de changer la classe utilisée pour TextMessage, en passant les mêmes paramètres. Vous pouvez aussi voir que la fonctionnalité de la direction générale de l'intention est de passer à false au lieu de true pour l' :include_timestamp option. Pour fusionner ces changements, combiner l'intention à la fois de:

TextMessage.send(:include_timestamp => false)

En général:

  • Comparer l'ancêtre commun à chaque branche, et de déterminer dans quelle branche a la plus simple de changer
  • Appliquer cette simple modification de l'autre branche de la version du code, de sorte qu'il contient à la fois le plus simple et le plus complexe du changement
  • Supprimer toutes les sections de conflit code autre que celui que vous venez de fusionner les modifications dans

373voto

VonC Points 414372

"Conflits" signifie "parallèle les évolutions d'un même contenu". Si elle va "tous en enfer" lors d'une fusion, cela signifie que vous avez massive des évolutions sur le même ensemble de fichiers.

La raison pour laquelle un rebase est alors de mieux qu'une fusion, c'est que:

  • vous réécrivez votre validation de l'histoire avec l'un des maître (et puis réappliquez votre travail, de résoudre les conflits à l'époque)
  • la dernière fusion sera certainement un "fast forward", car il dispose de toutes les commettre l'histoire du maître, plus que vos modifications à présenter une nouvelle demande.

Je confirme que le bon flux de travail dans ce cas (évolutions communes à un ensemble de fichiers) est rebase d'abord, puis les fusionner.

Toutefois, cela signifie que, si vous poussez votre succursale locale (pour la sauvegarde de la raison), que la branche ne doit pas être tiré (ou au moins utilisé) par quelqu'un d'autre (puisque la validation de l'histoire sera réécrite par l'successives rebase).


Sur ce sujet (rebase puis fusionner les flux de travail), barraponto mentionne dans les commentaires de deux postes intéressants, à la fois de randyfay.com:

En utilisant cette technique, votre travail va toujours sur le dessus de la publique comme un patch, c'est-à-jour avec les HEAD.

(une technique similaire existe pour le bazar)

33voto

Alex Gontmakher Points 714

Dans mon travail, je rebase autant que possible (et j'essaie de le faire souvent. Ne pas laisser les contradictions s'accumulent réduit considérablement la quantité et la gravité des collisions entre les branches).

Toutefois, même dans la plupart rebase à base de flux de travail, il y a une place pour les fusions.

Rappelons que l'opération de fusion crée un nœud qui a deux parents. Maintenant, considérons la situation suivante: j'ai deux entités indépendante brances A et B, et veulent maintenant de développer des choses sur la branche C qui dépend à la fois de A et de B, alors A et B sont prise en revue.

Ce que je fais, alors, est la suivante:

  1. Créer (et de départ) branche C sur le dessus de A.
  2. Fusionner avec B

Maintenant, succursale C comprend les variations de A et de B, et je peux continuer à la développer. Si je fais tout changement à Un, puis j'ai reconstruire le graphe des branches de la façon suivante:

  1. création d'une branche de T sur le haut d'Un
  2. de fusion T avec B
  3. rebase C sur T
  4. supprimer la branche T

De cette façon, je peux effectivement maintenir l'arbitraire des graphiques de branches, mais faire quelque chose de plus complexe que la situation décrite ci-dessus est déjà trop complexe, étant donné qu'il n'existe pas d'outils automatique à faire de l'année de référence lorsque le parent changements.

23voto

Scott Brown Points 191

NE PAS utiliser git push origin --mirror dans PRESQUE n'IMPORTE quelle CIRCONSTANCE.

Il n'a pas demander si vous êtes sûr que vous voulez faire, et vous feriez mieux d'être sûr, car il va effacer tous vos branches distantes ne sont pas sur votre zone locale.

http://twitter.com/dysinger/status/1273652486

13voto

Pat Notz Points 46841

Dans votre situation, je pense que votre partenaire est correct. Ce qui est intéressant au sujet de la relocalisation est qu'à l'extérieur de votre change de look comme ils tout s'est passé dans une séquence de nettoyage par eux-mêmes. Cela signifie

  • vos modifications sont très faciles à consulter
  • vous pouvez continuer à faire les beaux, les petits s'engage, et pourtant vous pouvez faire des séries de celles qui s'engage public (par la fusion en master) à la fois
  • quand vous regardez le public de la branche principale, vous verrez différents de la série de commits pour des caractéristiques différentes par différents développeurs, mais ils ne sont pas tous être mélangés

Vous pouvez toujours continuer à pousser votre privé branche de développement pour le dépôt distant pour le bien de la sauvegarde, mais les autres ne doivent pas considérer que comme un "public" de la branche, puisque vous serez la relocalisation. BTW, une simple commande pour faire cela est git push --mirror origin .

L'article de l'Emballage du logiciel via Git fournit un assez bon travail en expliquant les compromis dans la fusion rapport à la relocalisation. C'est un peu différent, mais le principe est le même, - elle vient essentiellement de déterminer si vos branches soient publics ou privés, et comment vous prévoyez de les intégrer dans la canalisation principale.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X