web-dev-qa-db-fra.com

Pourquoi tant de projets préfèrent "git rebase" à "git merge"?

L'un des avantages de l'utilisation d'un DVCS est le flux de travail edit-commit-merge (sur edit-merge-commit souvent appliqué par un CVCS). Autoriser chaque modification unique à être enregistrée dans le référentiel indépendamment des fusions garantit que le DAG reflète fidèlement le véritable pedigree du projet.

Pourquoi tant de sites Web parle-t-il de vouloir "éviter les validations de fusion"? La fusion avant la validation ou la modification de la base après la fusion ne rend-elle 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 pour créer une fusion s'engage. Pourquoi tant d'endroits parlent-ils d'un désir de voir une histoire de développement linéaire qui cache ces engagements de fusion?

71
Jace Browning

Les gens veulent éviter les validations de fusion car cela rend le journal plus joli. Sérieusement. Il ressemble aux journaux centralisés avec lesquels ils ont grandi et, localement, ils peuvent faire tout leur développement dans une seule branche. Il n'y a aucun avantage en dehors de ces esthétiques, et plusieurs inconvénients en plus de ceux que vous avez mentionnés, comme le fait de provoquer des conflits pour tirer directement d'un collègue sans passer par le serveur "central".

66
Karl Bielefeldt

En deux mots: git bisect

Un historique linéaire vous permet de localiser la source réelle d'un bug.


def bar(arg=None):
    pass

def foo():
    bar()

La branche 1 effectue une refactorisation telle que arg n'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 autant de chance. Si le bogue se manifeste comme un comportement inattendu, plutôt que comme une erreur de compilation, nous ne le trouverons peut-être pas avant une semaine ou deux. À ce moment, 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)

Donc, quand nous envoyons git bisect pour trouver le commit qui a cassé la build, il va identifier un commit de fusion. Eh bien, cela aide un peu, mais cela pointe essentiellement vers un paquet de commits, pas un seul. Tous les ancêtres sont verts. En revanche, avec le rebasage, 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, git bisect va pointer vers la validation de fonctionnalité exacte qui a interrompu 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 aggravé dans les grands projets que lorsque les responsables n'ont pas écrit tout le code, ils ne se souviennent donc pas nécessairement pourquoi un commit donné a été fait/à quoi servait chaque branche. Il est donc très utile d'identifier exactement le commit (et de pouvoir ensuite examiner les commits qui l'entourent).


Cela dit, je préfère (actuellement) toujours fusionner. Le reclassement sur une branche de publication vous donnera votre historique linéaire à utiliser avec git bisect, tout en conservant la véritable histoire du travail quotidien.

48
Izkata

En bref, parce que la fusion est souvent un autre endroit où quelque chose ne va pas, et elle n'a besoin de tourner qu'une fois pour que les gens aient très peur d'y faire face (une fois mordu deux fois timide, si vous voulez).

Supposons donc que nous travaillions sur un nouvel écran de gestion de compte, et 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 des comptes et je corrige le bogue avec les nouveaux comptes. Puisque nous traitons tous les deux avec des comptes, nous travaillons avec un code très similaire - peut-être avons-nous même dû ajuster les mêmes morceaux de code.

Maintenant, en ce moment, nous avons deux versions différentes mais pleinement fonctionnelles du logiciel. Nous avons tous les deux effectué un commit sur nos changements, nous avons tous deux soigneusement testé notre code, et indépendamment nous sommes très confiants d'avoir fait un travail formidable. 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 fonctionnels à un seul, un nouveau logiciel horriblement cassé, où votre gestion de compte ne fonctionne pas et les nouveaux comptes sont cassés et je ne sais même pas si l'ancien bogue est toujours là .

Peut-être que le logiciel était intelligent et il a dit qu'il y avait un conflit et a insisté pour que nous lui donnions des conseils. Eh bien, merde - je m'assois pour le faire et je vois que vous avez ajouté du 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. L'un ou les deux d'entre nous doivent prendre le temps de s'asseoir, de procéder à une fusion appropriée et éventuellement de retester tout le truc pour s'assurer que nous ne l'avons pas cassé.

Pendant ce temps, 8 autres gars commettent tous du code comme les sadiques qu'ils sont, j'ai fait quelques petites corrections de bugs et les ai soumis avant que je sache que nous avions un conflit de fusion, et mec ça semble être le bon moment pour faire une pause, et peut-être que vous sont partis pour l'après-midi ou coincés dans une réunion ou autre chose. Je devrais peut-être juste prendre des vacances. Ou changer de carrière.

Et donc, pour échapper à ce cauchemar, certaines personnes ont très peur de l'engagement (quoi de neuf, d'accord?). Nous sommes naturellement opposés au risque dans des scénarios comme celui-ci - sauf si nous pensons que nous sommes nulles et que nous allons tout gâcher de toute façon, auquel cas les gens commencent à agir avec un abandon téméraire. soupir

Alors voilà. Oui, les systèmes modernes sont conçus pour soulager cette douleur, et il est censé être capable de reculer facilement et de rebaser et de déprécier et de freebase et de hanglide et tout ça.

Mais c'est encore plus de travail, et nous voulons simplement appuyer sur le bouton du micro-ondes et faire un repas de 4 plats avant d'avoir le temps de trouver une fourchette, et tout cela semble tellement insatisfaisant - le code est un travail, il est productif, son significatif, mais gérer avec élégance une fusion juste ne compte pas.

Les programmeurs, en règle générale, doivent développer une grande mémoire de travail, puis ont tendance à oublier immédiatement tous ces noms indésirables et variables et la portée dès qu'ils ont terminé le problème, et la gestion d'un conflit de fusion (ou pire, un fusion mal gérée) est une invitation à se souvenir de votre mortalité.

16
BrianH

Le reclassement fournit un point de branchement mobile qui simplifie le processus de repoussage des modifications vers la ligne de base. Cela vous permet de traiter une branche de longue durée comme s'il s'agissait d'un changement local. Sans rebasage, les branches accumulent les modifications de la ligne de base qui seront incluses dans les modifications fusionnées vers la ligne de base.

La fusion laisse votre ligne de base au point de branchement d'origine. Si vous fusionnez quelques semaines de modifications par rapport à la ligne à partir de laquelle vous vous êtes dérivé, vous avez maintenant beaucoup de modifications à partir de votre point de branche, dont beaucoup seront dans votre ligne de base. Cela rend difficile l'identification de vos changements dans votre agence. Le fait de repousser les modifications à la ligne de base peut générer des conflits sans rapport avec vos modifications. En cas de conflits, 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 branchement vers la dernière révision de votre ligne de base. Tout conflit que vous rencontrerez sera pour votre seul changement. Pousser les changements est beaucoup plus simple. Les conflits sont traités dans la branche locale en effectuant un rebase supplémentaire. Dans le cas de poussées conflictuelles, le dernier à pousser leur changement devra résoudre le problème avec leur changement.

5
BillThor

Les outils automatisés s'améliorent pour s'assurer que le code de fusion se compile et s'exécute, évitant ainsi conflits syntaxiques, mais ils ne peuvent garantir l'absence de conflits logiques pouvant être introduits par fusion. 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 la ramification et la fusion, comme je le vois, c'est qu'elle donne un coup de pied au proverbial sur la route. Il 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 la correction des bugs est toujours plus rapide/moins chère lorsqu'ils sont frais. Au moment où toutes les branches de code commencent à fusionner, vous pouvez déjà oublier certaines des nuances des choses qui ont été faites.

Prenez les deux problèmes susmentionnés ensemble et vous pourriez vous retrouver dans une situation où il est plus simple et plus facile de faire travailler tout le monde sur le même tronc et de résoudre en permanence les conflits à mesure qu'ils surviennent, même s'ils ralentissent le développement actif.

4
MrFox

Un autre point pertinent est le suivant: avec le rebasage, je peux facilement choisir ou restaurer une fonctionnalité dans ma branche de publication.

0
Bruno Schäpper

Trois choses n'ont été dites dans aucune des réponses:

  • Différence à l'intérieur d'une branche:

    • La différence entre une paire arbitraire de validations dans une branche devient extrêmement difficile lorsqu'il y a des validations 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 tout conflit de fusion vous reste à faire sans le contexte de la validation spécifique responsable du conflit. Si vous rebase, la rebase s'arrêtera au moment où le conflit s'est produit et vous permettra de le résoudre dans ce contexte.
  • Nettoyage avant push:

    • Si vous avez fait une erreur de commit que vous devez corriger plus tard, si vous n'avez pas poussé le rebase interactif, vous pourrez combiner/diviser/changer/déplacer les commits. Bien que vous puissiez toujours le faire si vous avez fusionné, il devient très difficile de combiner/fractionner/modifier/déplacer à travers une frontière de fusion.
0
Catskul