Le squashing pull request casse-t-il l'algorithme de fusion de git?

16

Je travaille actuellement pour une entreprise qui utilise VSTS pour gérer le code git. La méthode "recommandée" de Microsoft pour fusionner une branche consiste à effectuer une "fusion de squash", ce qui signifie que toutes les validations pour cette branche sont écrasées dans une nouvelle validation incorporant toutes les modifications.

Le problème est, que se passe-t-il si je fais des changements dans une branche pour un élément de backlog, puis que je veux immédiatement commencer à faire des changements dans une autre branche pour un autre élément de backlog, et ces changements dépendent du jeu de changements de la première branche?

Je peux créer une branche pour cet élément de backlog et la baser sur la première branche. Jusqu'ici tout va bien. Cependant, quand vient le temps de créer une demande de tirage pour moi, la deuxième branche, la première branche a déjà été fusionnée dans master et parce que cela a été fait comme une fusion de squash, git signale un tas de conflits. C'est parce que git ne voit pas les validations d'origine sur lesquelles la deuxième branche était basée, il voit juste la fusion d'un seul gros squash et donc afin de fusionner la deuxième branche pour la maîtriser, il essaie de rejouer toutes les validations de la première branche dans sommet de la fusion de squash, provoquant de nombreux conflits.

Donc ma question est, existe-t-il un moyen de contourner cela (autre que de ne jamais baser une branche de fonctionnalité sur une autre, ce qui limite mon flux de travail) ou la fusion de squash brise-t-elle simplement l'algorithme de fusion de git?

Jez
la source

Réponses:

15

Avec Git, s'engage

  • sont immuables,
  • et former un graphique acyclique dirigé.

Le squashing ne combine pas les commits. Au lieu de cela, il enregistre une nouvelle validation avec les modifications de plusieurs autres validations. Le reclassement est similaire, mais ne combine pas les validations. L'enregistrement d'un nouveau commit avec les mêmes modifications qu'un commit existant est appelé réécriture de l'historique . Mais comme les commits existants sont immuables, cela doit être compris comme «écrire une histoire alternative».

La fusion essaie de combiner les modifications de deux historiques (branches) de commit à partir d'un commit ancêtre commun.

Regardons donc votre histoire:

                                 F  feature2
                                /
               1---2---3---4---5    feature1 (old)
              /
-o---o---o---A---o---o---S          master

A est l'ancêtre commun, 1–5 la branche de fonction d'origine, F la nouvelle branche de fonctionnalité et S la validation écrasée qui contient les mêmes modifications que 1–5. Comme vous pouvez le voir, l'ancêtre commun de F et S est A. En ce qui concerne git, il n'y a pas de relation entre S et 1–5. Ainsi, la fusion de master avec S d'un côté et feature2 avec 1–5 de l'autre sera en conflit. Résoudre ces conflits n'est pas difficile, mais c'est un travail inutile et fastidieux.

En raison de ces contraintes, il existe deux approches pour gérer la fusion / écrasement:

  • Soit vous utilisez la réécriture de l'historique, auquel cas vous obtiendrez plusieurs validations qui représentent les mêmes changements. Vous devez ensuite rebaser la deuxième branche de fonctionnalité sur la validation écrasée:

                                     F  feature2 (old)
                                    /
                   1---2---3---4---5    feature1 (old)
                  /
    -o---o---o---A---o---o---S          master
                              \
                               F'       feature2
    
  • Ou vous n'utilisez pas la réécriture de l'historique, auquel cas vous pourriez obtenir des commits de fusion supplémentaires:

                                     F  feature2
                                    /
                   1---2---3---4---5    feature1 (old)
                  /                 \
    -o---o---o---A---o---o-----------M  master
    

    Lorsque feature2 et master sont fusionnés, l'ancêtre commun sera validé 5.

Dans les deux cas, vous aurez un effort de fusion. Cet effort ne dépend pas beaucoup de laquelle des deux stratégies ci-dessus vous choisissez. Mais assurez-vous que

  • les branches sont de courte durée, pour limiter la distance à laquelle elles peuvent dériver de la branche principale, et que
  • vous fusionnez régulièrement master dans votre branche de fonctionnalité, ou rebasez la branche de fonctionnalité sur master pour garder les branches synchronisées.

Lorsque vous travaillez en équipe, il est utile de coordonner qui travaille actuellement sur quoi. Cela permet de limiter le nombre de fonctionnalités en cours de développement et de réduire le nombre de conflits de fusion.

amon
la source
2
Votre réponse ne semble pas s'attaquer à ce qui se passe si vous fusionnez d'abord feature1sur le maître, puis souhaitez fusionner feature2plus tard. Dans ce cas, la première approche n'entraînerait-elle pas de conflits alors que git essaie de feature1réappliquer les validations au-dessus de la validation écrasée, tandis que la seconde permettrait à git de déterminer que ces validations n'ont pas besoin d'être fusionnées?
Jez
@Jez C'est exactement ce qui se passe lorsque vous écrasez un PR. J'ai récemment dû réécrire manuellement un PR sur un projet OSS (en git cloneing le repo et en copiant mes fichiers modifiés!) Parce que j'ai ramifié à partir d'une branche puis le mainteneur a écrasé la première branche. À mon travail, ils font également des fusions de squash. Cela signifie que je ne peux pas travailler sur une fonctionnalité bqui dépend de la fonctionnalité atant que la fonctionnalité an'est pas fusionnée.
Compte à jeter le
1
Et n'est-ce pas une rupture vraiment ennuyeuse de quelque chose qui fonctionnerait autrement, comme Git est conçu pour? Vous voyez, je vois diverses organisations comme Microsoft et Github recommander réellement ces fusions de squash et elles me semblent stupides.
Jez
1
@Jez Dans le scénario d'origine, oui, vous obtiendrez des conflits lors de la fusion de feature2 dans master car la fusion des validations 1 à 5 entrera en conflit avec les mêmes changements dans S. La solution consiste à rebaser feature2 (solution 1) ou à ne pas utiliser le squashing / rebasing à tous (solution 2).
amon
Que les fusions de squash vous conviennent le mieux dépend de ce que vous souhaitez enregistrer dans l'historique du contrôle de version. Si les branches de fonctionnalité ont de nombreuses validations WIP, le squashing place une seule grosse validation avec la fonctionnalité complète sur la branche principale. Si vous préférez conserver toutes les validations intermédiaires de la branche de fonctionnalité, utilisez le rebasage ou la fusion.
amon
11

La fusion de squash interrompt l'algorithme de fusion pour toutes les branches contenant des commits supprimés par le squash. Autrement dit, les rebases sont virales. Si vous rebase une branche, vous devez rebaser toutes les autres branches qui dépendent de cette branche. Si vous utilisez rerere , les conflits de fusion que vous avez résolus manuellement dans votre référentiel local n'auront plus besoin d'être résolus manuellement, mais cela n'aide pas les conflits résolus par d'autres personnes.

C'est pourquoi notre règle non écrite ici est qu'il est correct de squash tant que personne d'autre n'a jamais dépendu de votre branche de fonctionnalité, ce qui est peut-être le cas dans 90% des cas. Sinon, une fusion directe aide tout le monde à éviter les problèmes.

Karl Bielefeldt
la source
Une façon d'avoir un commit écrasé dans l'historique maître et une branche de fonctionnalité intacte pour créer une branche squash uniquement distincte. Supposons que vous ayez une feature-xyzbranche. Vous pouvez créer une feature-xyz-squashedbranche commençant au même commit que la feature-xyzbranche, git cherry-pickles validations de feature-xyzà feature-xyz-squashed, les écraser là et fusionner feature-xyz-squashedvers master. Vous ne devriez pas fusionner feature-xyzalors. Parfois, ce qui précède est logique (par exemple, vous ne voulez pas inclure les validations avec un mot de passe intégré), mais c'est une solution de contournement, à peine une meilleure pratique.
9000