Pourquoi tant de projets préfèrent-ils “git rebase” à “git merge”?

69

L'un des avantages de l'utilisation d'un DVCS est le flux de travail edit-commit-merge (par rapport à edit-merge-commit souvent appliqué par un CVCS). En autorisant chaque modification unique à être enregistrée dans le référentiel indépendamment des fusions, le DAG reflète avec précision le véritable pedigree du projet.

Pourquoi tant de sites parlent-ils de vouloir "éviter les commits de fusion"? La fusion pré-commit ou la refonte post-fusion ne rendent-elles pas plus difficile l'isolement des régressions, l'annulation des modifications passées, etc.?

Point de clarification: le comportement par défaut d'un DVCS est de créer des commits de fusion. Pourquoi tant d’endroits parlent-ils du désir de voir une histoire de développement linéaire qui cache ces engagements de fusion?

Jace Browning
la source
18
Les outils ne fonctionnent pas toujours correctement. Et quand ils se trompent, oh boy, est-ce qu'ils se trompent?
Oded
2
@Oded faites-vous référence aux commits de fusion automatiques (tels que ceux créés par git pull)? Je peux comprendre pourquoi cela pourrait poser problème, mais ma question concerne tous les commits de fusion.
Jace Browning
doublon possible de Les conflits de fusion fréquents et compliqués sont-ils un signe de problèmes? "Les fusions et les rebases devraient provoquer exactement les mêmes conflits, pour les conflits inhérents qu'un humain doit résoudre (c'est-à-dire que deux développeurs changent la même ligne de code) ..."
jeudi
Une pensée que j’ai eue après avoir posté ma réponse, c’est que l’OMI ne correspond pas exactement à la réponse: git pull --rebase == svn up(égaux lâches, pas égaux stricts)
Izkata
dans un magasin centralisé comme SourceSafe, les modifications continueraient à être archivées dans le cadre d'un effort continu, mais une fois qu'un plateau stable aurait été atteint, tous les fichiers seraient étiquetés à une date donnée. Alors au moins, vous pouvez corréler les bogues avec soit avant, soit après telle version, ou rebase - line .
Andyz Smith

Réponses:

65

Les gens veulent éviter les commits de fusion parce que cela rend le journal plus joli. Sérieusement. Cela ressemble aux journaux centralisés avec lesquels ils ont grandi et localement, ils peuvent faire tout leur développement dans une seule branche. Outre cette esthétique, il n'y a aucun avantage, et plusieurs inconvénients en plus de ceux que vous avez mentionnés, tels que le rendre sujet à des conflits est directement lié à un collègue sans passer par le serveur "central".

Karl Bielefeldt
la source
5
Vous êtes le seul à avoir bien compris ma question. Comment puis-je le rendre plus clair?
Jace Browning
12
Peut-être reformulez-vous "Quels sont les avantages d'une histoire de développement linéaire?"
Karl Bielefeldt
11
"Souvent, vous le ferez pour vous assurer que vos commits s'appliquent correctement dans une branche distante - peut-être dans un projet auquel vous essayez de contribuer mais que vous ne maintenez pas. Dans ce cas, vous feriez bien votre travail." dans une branche, puis redéfinissez votre travail sur origin / master lorsque vous êtes prêt à soumettre vos correctifs au projet principal, de sorte que le responsable de la maintenance n’a pas à effectuer de travail d’intégration - il suffit d’appliquer une application rapide. " git-scm.com/book/fr/Git-Branching-Rebasing
Andyz Smith
12
Vous donnez l'impression que c'est simplement esthétique, mais c'est en fait pratique: il est beaucoup plus facile de comprendre ce qui se passe dans une histoire linéaire que dans un DAG complexe avec des lignes qui se croisent et se confondent.
Daniel Hershcovich
20
"Il n'y a aucun avantage en dehors de cette esthétique" - je n'en sais rien. Il est très difficile de comprendre ce qui se passe dans votre histoire lorsque celle-ci ressemble à un circuit imprimé. (En supposant que plusieurs membres de l'équipe possèdent chacun leurs propres branches et flux de travail.)
Archagon
46

En deux mots: git bisect

Un historique linéaire vous permet d'identifier la source réelle d'un bogue.


Un exemple. Ceci est notre code initial:

def bar(arg=None):
    pass

def foo():
    bar()

La branche 1 effectue une refactorisation telle qu’elle argn’est plus valide:

def bar():
    pass

def foo():
    bar()

La branche 2 a une nouvelle fonctionnalité qui doit utiliser arg:

def bar(arg=None):
    pass

def foo():
    bar(arg=1)

Il n'y aura pas de conflits de fusion, cependant un bogue a été introduit. Heureusement, celui-ci sera pris dans l’étape de compilation, mais nous n’avons pas toujours cette chance. Si le bogue se manifeste par un comportement inattendu plutôt que par une erreur de compilation, il se peut que vous ne le trouviez pas avant une semaine ou deux. À ce point, git bisectà la rescousse!

Oh merde. Voici ce qu'il voit:

(master: green)
|             \_________________________
|                \                      \
(master: green)  (branch 1: green)     (branch 2: green)
|                 |                     |
|                 |                     |
(master/merge commit: green)            |
|                         ______________/
|                        /
(master/merge commit: red)
|
...days pass...
|
(master: red)

Ainsi, lorsque nous envoyons git bisectchercher le commit qui a cassé la construction, il va localiser un commit de fusion. Cela aide un peu, mais il s’agit essentiellement d’un paquet d’engagements, pas d’un seul. Tous les ancêtres sont verts. Par contre, avec le rebasement, vous obtenez un historique linéaire:

(master: green)
|
(master: green)
|
(all branch 1 commits: green)
|
(some branch 2 commits: green)
|
(branch 2 commit: red)
|
(remaining branch 2 commits: red)
|
...days pass...
|
(master: still red)

Maintenant, nous git bisectallons pointer sur la fonctionnalité exacte qui a cassé la construction. Idéalement, le message de validation expliquera ce qui était prévu pour faire un autre refactor et corriger le bogue immédiatement.

L'effet n'est composé que dans les grands projets lorsque les responsables n'ont pas écrit tout le code; ils ne se souviennent donc pas nécessairement pourquoi une validation donnée a été effectuée / à quoi servait chaque branche. Il est donc très utile d’identifier le commit exact (puis de pouvoir examiner les commits autour de celui-ci).


Cela dit, je préfère (actuellement) encore fusionner. En vous basant sur une branche de publication, vous obtiendrez votre historique linéaire git bisect, tout en conservant le véritable historique pour le travail quotidien.

Izkata
la source
2
Merci pour cette explication. Pouvez-vous expliquer pourquoi vous préférez toujours fusionner? Je sais que beaucoup de gens préfèrent fusionner au rebasement dans la plupart des situations, mais je n'ai jamais compris pourquoi.
Philip
@Philip Je suis un peu hésitant à le faire, je n'ai utilisé git que pendant quelques mois sur de petits projets personnels, jamais sur une équipe. Mais lors du suivi de ce que j’ai fait récemment, il gitk --allest extrêmement utile de voir le flux des commits dans les différentes branches (d’autant plus que j’ai habituellement trois branches à la fois, et que je les bascule chaque jour en fonction de mon humeur chez le temps).
Izkata
16
Mais la fusion est le problème, vous voulez donc une validation de fusion en bisectconséquence. Il est assez facile de trouver les commits individuels avec un blameou un autre bisectsi vous voulez aller plus loin. Avec une histoire linéaire, la fusion se fait hors livre, vous ne pouvez donc plus dire qu'une mauvaise fusion était le problème. Cela ressemble à un idiot qui utilise un argument qui n'existe pas, surtout si l'argument a été supprimé plusieurs fois avant. Essayer de comprendre pourquoi il ferait cela est beaucoup plus difficile lorsque vous détruisez l'histoire.
Karl Bielefeldt
1
Vraiment en désaccord avec cette réponse. Rebaser détruit parfois l'utilité de la bissect. À l'exception des commits de tête d'une base, les commits d'une base peuvent ne pas être exécutables. Exemple: Vous avez créé une branche en A et créé B, C, D, quelqu'un poussé E. Vous êtes B et C et vous travaillez, mais vous vous êtes basé sur E, vous êtes B et C, vous êtes inamovibles ou vous ne pouvez pas fonctionner. Le meilleur des cas est que vous abandonniez, dans le pire des cas, vous pensez que B ou C contient le problème alors qu'en réalité, il s'agit de D.
Lan
4
@Izkata Pourquoi utilisez-vous une bissectrice? Parce que des bugs / erreurs se produisent. C'est une réalité humiliante. Peut-être que "faire glisser la souris vers la gauche" ne faisait pas partie de la liste des tests automatisés / manuels auparavant et nous remarquons maintenant que cette fonctionnalité est défectueuse, mais nous savons qu'elle fonctionnait auparavant.
Lan
17

En bref, parce que la fusion est souvent un autre endroit où quelque chose ne va pas, et il suffit de le faire une seule fois pour que les gens aient très peur d’en avoir à nouveau (une fois mordu deux fois, si vous voulez).

Supposons donc que nous travaillons sur un nouvel écran de gestion de compte et qu'il s'avère qu'un bogue a été découvert dans le flux de travail Nouveau compte. OK, nous prenons deux chemins distincts: vous terminez la gestion de compte et je corrige le bogue avec de nouveaux comptes. Puisque nous traitons tous les deux avec des comptes, nous travaillons avec un code très similaire - nous avons peut-être même dû ajuster les mêmes éléments de code.

Nous disposons actuellement de deux versions différentes mais parfaitement opérationnelles du logiciel. Nous avons tous deux validé nos modifications, testé consciencieusement notre code et, indépendamment, nous sommes très confiants d'avoir accompli un travail remarquable. Maintenant quoi?

Eh bien, il est temps de fusionner, mais ... merde, que se passe-t-il maintenant? Nous pourrions très bien passer de deux ensembles de logiciels de travail à un, un logiciel récemment bogué, unifié et horriblement cassé, dans lequel la gestion de votre compte ne fonctionne pas et où les nouveaux comptes sont brisés et je ne sais même pas si l'ancien bogue existe toujours. .

Peut-être que le logiciel était intelligent et qu'il disait qu'il y avait un conflit et insistait pour que nous le guidions. Eh bien, merde - je m'assieds pour le faire et vois que vous avez ajouté un code complexe que je ne comprends pas immédiatement. Je pense que cela entre en conflit avec les modifications que j'ai apportées ... Je vous le demande, et quand vous avez une minute, vous vérifiez et vous voyez mon code que vous ne comprenez pas. Nous devons prendre le temps de nous asseoir, de procéder à une fusion correcte et éventuellement de réessayer le problème pour nous assurer de ne pas le rompre.

En attendant, 8 autres types utilisent tous le même code que les sadiques, j’ai fait quelques petites corrections de bugs et les ai soumises avant que je sache que nous avions un conflit de fusion et il me semble que c’est un bon moment pour faire une pause, et peut-être vous sont partis pour l'après-midi ou coincés dans une réunion ou autre chose. Peut-être que je devrais juste prendre des vacances. Ou changer de carrière.

Et ainsi, pour échapper à ce cauchemar, certaines personnes ont très peur de l'engagement (quoi de neuf, de nouveau?). Nous sommes naturellement peu enclins à prendre des risques dans des scénarios comme celui-ci - à moins que nous ne pensions que nous soyons nuls et que nous allions le gâcher quand même, auquel cas les gens commenceraient à agir avec un abandon inconsidéré. soupir

Alors voilà. Oui, les systèmes modernes sont conçus pour atténuer cette douleur, et il est supposé pouvoir facilement faire marche arrière et se rebaser et se rabaisser, ainsi que la freebase et la hanglide.

Mais tout cela demande plus de travail, et nous voulons simplement appuyer sur le bouton du four à micro-ondes et préparer un repas de 4 plats avant d'avoir le temps de trouver une fourchette. Tout cela semble si insatisfaisant. significatif, mais gérer avec élégance une fusion ne compte tout simplement pas.

En règle générale, les programmeurs doivent développer une excellente mémoire de travail, puis ont tendance à immédiatement oublier tous ces noms de junk et de variable et leur champ d’application dès qu’ils ont résolu le problème, et lorsqu’un conflit de fusion fusion mal gérée) est une invitation à vous rappeler votre mortalité.

BrianH
la source
5
Étonnamment libellé, monsieur!
Lièvre Southpaw
10
Cela répond à la raison pour laquelle les gens ont peur de la fusion et non pas à la raison pour laquelle beaucoup commettent des commits de fusion.
Jay
23
Le problème avec cette réponse est que cela est toujours vrai pour une base . Après tout, une base est toujours en train de fusionner: vous avez modifié une ligne de code déjà modifiée. Je pense que c'est un problème avec la façon dont la question a été posée.
deworde
2
J'ai voté contre cette réponse car, comme elle est toujours éloquente, elle dépasse le propos, à mon humble avis.
Eugene
6
Cela ne semble pas toucher à la base vs fusion-fusion du tout.
Andyz Smith
5

Le changement de base fournit un point de branche mobile qui simplifie le processus de transfert des modifications vers la ligne de base. Cela vous permet de traiter une branche longue en cours d'exécution comme s'il s'agissait d'un changement local. Sans refonte de la base, les branches accumulent les modifications par rapport à la référence, qui seront incluses dans les modifications fusionnées à la référence.

La fusion laisse votre ligne de base au point de branche d'origine. Si vous fusionnez quelques semaines de modifications par rapport à la ligne sur laquelle vous avez dérivé, vous avez maintenant de nombreux changements à partir de votre point de branche, dont beaucoup se trouveront dans votre ligne de base. Cela rend difficile l'identification de vos modifications dans votre branche. Le fait de repousser les modifications dans la base peut générer des conflits sans lien avec vos modifications. En cas de conflit, il est possible de pousser des modifications incohérentes. Les fusions en cours nécessitent des efforts de gestion et il est relativement facile de perdre des modifications.

Rebase déplace votre point de branche vers la dernière révision de votre référence. Tous les conflits que vous rencontrerez seront uniquement pour vos changements. Pousser les changements est beaucoup plus simple. Les conflits sont traités dans la branche locale par une nouvelle base. Dans le cas de poussées contradictoires, le dernier à pousser leur changement devra résoudre le problème avec leur changement.

BillThor
la source
1
il ne fait donc que rétrograder les nouveaux changements à un niveau où ils sont considérés comme «la mauvaise chose», car ils sont le seul changement . Si tous les anciens changements sont inclus dans la fusion, tous les changements sont sur un pied d'égalité, en ce qui concerne s'ils sont «la bonne chose» aujourd'hui.
Andyz Smith
@AndyzSmith Je ne considérerais pas les nouveaux changements comme une mauvaise chose . En essayant de déterminer ce qui est en train de changer et qui devrait être poussé, c'est la bonne chose . Une fois que vous avez redéfini la base, vous pouvez ignorer les anciennes modifications car elles font partie de votre base.
BillThor
Si vous ne savez pas quelle est votre base de référence, c’est-à-dire si ce que vous avez déjà modifié et prêt à être fusionné est la «bonne chose à faire», ne rebasonnez pas.
Andyz Smith
Et si vous obtenez un changement de pression de la part de quelqu'un et que cela brise la construction parce que son prototype de fonction ne correspond pas à un prototype existant, vous savez instantanément, à cause de la modification de la base, que le nouveau type a tort. vous n'avez pas à deviner que le nouveau type a peut-être le bon prototype et que les modifications apportées précédemment au système de jeu de changements, sans application ni retrait, sont celles avec le mauvais prototype.
Andyz Smith
4

Les outils automatisés s'assurent de mieux en mieux que le code de fusion soit compilé et exécuté, évitant ainsi les conflits syntaxiques , mais ils ne peuvent garantir l'absence de conflits logiques pouvant être introduits par les fusions. Ainsi, une fusion «réussie» vous donne un sentiment de fausse confiance, alors qu'en réalité elle ne garantit rien et que vous devez refaire tous vos tests.

Le vrai problème avec les ramifications et les fusions, à mon avis, est que cela donne le coup d'envoi à la proverbiale possibilité. Cela vous permet de dire "Je vais juste travailler dans mon propre petit monde" pendant une semaine et de régler les problèmes qui surviendront plus tard. Mais réparer les bugs est toujours plus rapide / moins cher quand ils sont frais. Au moment où toutes les branches de code commencent à être fusionnées, vous pouvez déjà oublier certaines des nuances de ce qui a été fait.

Si vous combinez les deux problèmes susmentionnés, vous vous retrouverez peut-être dans une situation où il est plus simple et plus simple de laisser tout le monde travailler dans le même coffre et de résoudre en permanence les conflits à mesure qu'ils surviennent, même s'ils ralentissent un peu le développement actif.

MrFox
la source
4
Cela répond à la raison pour laquelle les gens ont peur de la fusion et non pas à la raison pour laquelle beaucoup commettent des commits de fusion.
Jay
Donc, cette malle à laquelle vous faites référence, est-ce que cela implique un rebasement? Exposez s'il vous plaît.
Andyz Smith
@AndyzSmith "trunk" est le terme utilisé par svn pour son équivalent à la branche "master" de git
Izkata
@ Izkata donc cette réponse préconise de ne pas utiliser ni fusion ni refonte?
Andyz Smith
@AndyzSmith Ouais, c'est comme ça que je l'ai lu aussi. Et je ne suis pas d'accord avec ça. après avoir travaillé en équipe avec 7 autres développeurs de la manière suggérée, je ne le suggère pas. Nous aurions dû utiliser davantage de branches de fonctionnalités que nous, mais la plupart d’entre elles ont peur de fusionner (comme le dit BrianDHall dans sa réponse).
Izkata
0

Un autre point pertinent est le suivant: avec le changement de base, je peux facilement sélectionner ou annuler une fonctionnalité dans ma branche de publication.

Bruno Schäpper
la source
1
Ceci élaborer sur ceci?
Akavall
Bien sûr :-) Dans git flow, nous créons régulièrement de nouvelles branches de publication. Si après la création, un bogue est corrigé dans la branche de développement, nous l’ git cherry-pick -x <commit>appliquons à la branche de publication. Ou, nous pourrions vouloir annuler quelque chose pour la publication, avec git revert <commit>. Si <commit>c'est une fusion, ça devient poilu. Il est possible pour autant que je sache, mais pas facile à faire. Lorsque vous utilisez rebase, chaque "fusion" est un géant mais un commit régulier, facilement sélectionnable et réversible.
Bruno Schäpper
0

Trois choses ne sont dites dans aucune des réponses:

  • Diffing à l'intérieur d'une branche:

    • La distinction entre une paire arbitraire de commits dans une branche devient extrêmement difficile lorsqu'il existe des commits de fusion.
  • Fusionner un élément à la fois:

    • Lorsque vous résolvez la différence entre deux branches, une fusion se produit généralement en une seule fois et tous les conflits de fusion que vous devez résoudre ne sont pas liés au contexte dans lequel le commit spécifique était responsable du conflit. Si vous redéfinissez la base, celle-ci s’arrête au point où le conflit s’est produit et vous permet de le résoudre dans ce contexte.
  • Nettoyage avant poussée:

    • Si vous avez commis une erreur de validation que vous devez corriger par la suite, si vous n'avez pas poussé de réabonnement interactif, vous pourrez combiner / diviser / modifier / déplacer des commits. Même si vous pouvez toujours le faire si vous avez fusionné, il devient très difficile de combiner / scinder / changer / déplacer à travers une limite de fusion.
Catskul
la source