Stratégies pour la fusion d'un an de développement dans Visual Studio

32

Un de mes clients a insisté pour que notre nouveau développement soit séparé des principales succursales pendant toute l'année 2016. Trois ou quatre autres équipes travaillaient sur l'application à divers titres. De nombreuses modifications importantes ont été apportées (modification de l'injection de dépendance, nettoyage du code avec ReSharper, etc.). Il m'est maintenant imposé de fusionner la division principale de notre nouvelle branche de développement pour nous préparer à faire avancer nos changements dans la chaîne.

Lors de ma première fusion, TFS a signalé ~ 6500 fichiers avec résolution de conflit. Certaines d’entre elles seront faciles, mais d’autres beaucoup plus difficiles (en particulier javascript, contrôleurs api et services prenant en charge ces contrôleurs).

Y a-t-il une approche que je peux prendre qui me facilitera la tâche?

Pour clarifier, j’ai exprimé de nombreuses inquiétudes à propos de cette approche à plusieurs reprises. Le client était et est conscient des difficultés que cela pose. Parce qu’ils ont choisi de réduire le personnel d’assurance qualité (1 testeur pour 4 développeurs, pas de tests automatisés, peu de tests de régression), ils ont insisté pour que nous isolions notre branche des changements dans la branche principale sous prétexte que cela réduirait la nécessité de notre système. testeur pour savoir si des modifications ont été apportées ailleurs.

L’un des problèmes les plus importants ici est la mise à niveau de la version angulaire et de certains logiciels tiers. Malheureusement, nous n’avons trouvé aucun moyen de construire cette solution tant que tous les éléments ne seront pas remis en place.

utilisateur258451
la source
63
Nan. Vous avez deux branches distinctes qui ont été activement développées pendant un an. La fusion va sucer.
17 du 26
2
Quelle est l'ampleur des changements apportés par votre équipe? Il peut être plus efficace d'identifier ces modifications, puis de les réappliquer manuellement à la base de code maître actuelle.
kdgregory
2
Est-ce l'intégralité de 2015 ou 2016? Si c'est 2015 c'est 2 ans, ce qui signifie qu'il va double sucer.
David dit: Réintégrer Monica le
1
Pourquoi le client s’occupe-t-il si le travail que vous effectuez pour lui se trouve dans une branche distincte de votre système de contrôle de version?
Ixrec
17
Quoi que vous fassiez, assurez-vous de facturer toutes les heures.
Sean McSomething

Réponses:

37

Il y aurait eu un moyen simple de séparer votre nouveau développement de la branche principale sans vous placer dans cette situation regrettable: toute modification du coffre aurait dû être fusionnée quotidiennement dans votre branche de développement . (Votre client était-il vraiment si myope qu'il ne pouvait pas anticiper le besoin de réintégrer votre agence dans la ligne principale un jour?)

Quoi qu'il en soit, la meilleure approche est IMHO en essayant de refaire ce qui aurait dû se passer de première main:

  • identifier la sémantique des modifications sur la ligne principale pour le jour 1 suivant la création de la branche. Appliquez-les à votre base de code actuelle aussi bien que vous le pouvez. Si c'était un "changement local", cela devrait être simple, s'il s'agissait d'un "refactoring transversal" comme le changement de nom d'une classe largement utilisée, appliquez-la de manière sémantique à votre base de code actuelle. Espérons qu'au cours de cette année aucun changement transversal contradictoire dans la base de code n'a été effectué sur "votre" branche, sinon cela pourrait devenir un véritable casse-tête.
  • tester le résultat (ai-je mentionné que vous aviez besoin d'une bonne suite de tests pour cette tâche)? Corrige tous les bugs révélés par le test
  • Répétez maintenant cette procédure pour les modifications apportées à la ligne principale pour le jour 2, puis le jour 3, etc.

Cela peut fonctionner lorsque les équipes se conforment strictement aux règles classiques du contrôle de version ("ne commettre que des états compilables et testés" et "se connecter tôt et souvent").

Après 365 répétitions (ou 250 si vous avez de la chance et que vous pouvez regrouper le travail pour les modifications du week-end), vous aurez presque terminé (presque, car vous devez ajouter le nombre de modifications apportées à la ligne principale pendant la période d'intégration. ). La dernière étape consistera à fusionner à nouveau la branche de développement mise à jour dans le coffre (afin de ne pas perdre l'historique de ce dernier). Cela devrait être facile, car techniquement, cela ne devrait remplacer que les fichiers affectés.

Et oui, je suis sérieux, il n’ya probablement pas de raccourci pour cela. Il se peut que les "portions quotidiennes" soient parfois trop petites, mais je ne m'attendrais pas à cela. Je suppose qu'il est plus probable que les portions quotidiennes deviennent trop grosses. J'espère que votre client vous paye vraiment bien pour cela, et que cela lui coûte tellement cher qu'il va apprendre de son échec.

Je devrais ajouter que vous pouvez également essayer ceci avec des côtés inversés - en réintégrant les modifications de votre branche par petites portions jusqu'à la ligne principale. Cela est peut-être plus simple lorsque sur votre branche dev, le nombre de modifications est beaucoup moins important que sur le tronc ou que la plupart des modifications ont eu lieu dans de nouveaux fichiers source qui ne font actuellement pas partie du tronc. On peut voir cela comme un "portage" d'une fonctionnalité d'un produit A (la branche dev) vers un produit quelque peu différent (état actuel du tronc). Mais si la majorité des refactorisations transversales ont été effectuées sur la ligne principale et qu'elles affectent votre nouveau code (les collisions de fusion 6500 semblent en être une preuve), il serait peut-être plus facile de le décrire en premier.

Doc Brown
la source
9
Si vous poursuivez le développement du tronc pendant la réintégration, je vous suggère de créer une branche du tronc en premier et de développer à partir de cela. Sinon, vous fusionnez efficacement le passé et le futur simultanément. Mais je m'en remets naturellement à Doc Brown pour toute discontinuité spatio-temporelle.
radarbob
1
@radarbob: ce que vous suggérez n'a de sens que si le PO s'intègre de dev à trunk, mais pas lorsqu'il décide de fusionner de trunk en dev, comme je l'ai décrit en premier. Si l'OP transfère les modifications d'un tronc à sa branche dev, jour après jour (en commençant par les modifications 365 jours auparavant), peu importe si le développement sur le tronc se poursuit. Il devra juste continuer cette tactique jusqu'à ce qu'il atteigne le présent (supposé qu'il peut intégrer et tester les changements de ces 3-4 équipes un jour en moins d'un jour).
Doc Brown le
Citation: "Je devrais ajouter que vous pouvez aussi essayer cela avec les côtés inversés - en réintégrant les changements de votre branche dans les faisceaux quotidiens vers la ligne principale. " Mon sens spidey fait fondre. Je sens une cascade potentielle de "distorsion de résonance harmonique" avec des intégrations de changement contradictoires.
radarbob
C'est un bon conseil. Voir éditer pour aborder un certain nombre de choses ici.
user258451
1
@ user258451: vous aviez donc différentes équipes d'assurance qualité pour le coffre et la nouvelle branche de développement en place qui ne voulaient pas se parler? Great Scott: - ((
Doc Brown
14

À ce stade de la fusion, je dirais que la fusion automatisée ne peut que trop compliquer le processus. J'ai eu des problèmes similaires avec les branches qui ont divergé pendant plus d'un an et la méthode la plus efficace que j'ai est de faire ce qui suit:

  • Prenez une copie de l'état d'origine non fusionné
  • Diff entre le non-fusionné et le dernier
  • Décomposer les éléments communs
    • Par exemple, tous les changements de nom de fonction, puis de paramètre, etc.
    • Ignorer les espaces blancs dans les différences si les normes ont changé, sinon vous perdrez beaucoup de temps à compter les espaces
  • Concentrez-vous d'abord sur la fonctionnalité principale

Finalement, les avertissements du compilateur et les diffs seront vos meilleurs amis, continuez à utiliser le diff non fusionné pour voir exactement ce qui était différent et continuez comme ça. Vous pouvez utiliser divers outils pour vous aider, mais ce sera à vous de choisir le meilleur.

La clé est de continuer.

Modifier:

Avertissement: cette approche signifiera que l'historique de contrôle de version deviendra "corrompu", car vous perdriez la preuve de la fusion de branche à branche, ainsi que de l'historique de la branche non fusionnée.

Merci aux commentaires de 'Jack Aidley' et '17 of 26 '

Erdrik Ironrose
la source
1
Le problème majeur de cette approche est qu’elle détruira l’enregistrement des modifications laissées dans le système de contrôle de version.
Jack Aidley
Essentiellement, en effectuant toutes les mêmes modifications une seconde fois lors de la fusion, vous auriez toujours un journal des modifications, il s'agirait simplement de l'ordre effectué lors de la fusion plutôt que de l'ordre dans lequel elles ont été effectuées lors du développement. Pas idéal, mais mieux que rien. De plus, vous auriez toujours l'état non fusionné d'origine, s'il était conservé dans le contrôle de version.
Erdrik Ironrose
Vous auriez un journal des modifications, mais vous n'auriez aucune preuve historique que les modifications ont été fusionnées d'une branche à l'autre. Ce serait une énorme affaire dans TFS, qui ne vous propose que les ensembles de modifications non fusionnés à choisir lors de la fusion de branche à branche.
17 du 26
@ 17 sur 26 Bien que je convienne que vous pouvez restaurer un enregistrement des modifications, 17 sur 26 a raison de dire que cet enregistrement ne sera ni complet ni précis. Il se peut que cette approche soit suffisamment facile pour faire de cette perte de record un compromis acceptable compte tenu de la mauvaise situation dans laquelle ils se trouvent. Cependant, je pense que reconnaître quel que soit le choix que l'on fait est un inconvénient important.
Jack Aidley
1
@Jack Aidley Je suis tout à fait d'accord avec ça, c'est tout le problème, alors j'ai ajouté un peu à ma réponse pour réfléchir. J'espère que ça va?
Erdrik Ironrose
8

Il y a quelques années, nous avions un client avec les mêmes exigences de séparation des branches. Donc nous l'avons fait.

Nous n'avons jamais fusionné leur branche en arrière. Ils avaient leur propre version unique. Nous leur avons facturé des frais supplémentaires pour les modifications, car nous avions essentiellement deux principaux troncs au lieu d’un tronc principal et de branches.

Nous avons tenté de fusionner notre système, mais après deux semaines, nous avons décidé d'abandonner cet effort, qui durait des heures sans aucun bénéfice tangible.

Alors, ne le fusionnez pas en arrière. Vous pouvez désormais fusionner les correctifs critiques pour cette branche cliente au besoin. Toute amélioration apportée serait facturée spécifiquement à ce client.

Jon Raynor
la source
Cette stratégie n'est réalisable que si les différentes lignes de développement ciblent différents clients. Ou, si les cas d'utilisation où le produit est impliqué permettent d'utiliser les deux lignes de produits différentes en parallèle, de manière non intégrée. À ma connaissance, le PO décrit une situation dans laquelle la nouvelle ligne de développement cible le même client que celui qui utilise le coffre, et il n’est pas clair si l’utilisation parallèle de deux lignes de produits pourrait avoir un sens pour son cas.
Doc Brown
1

Ce ne sera pas amusant, mais sa douleur dépend de la nature des changements et de leur isolement.

Je vous suggère d'essayer de faire converger les branches par le biais de la refactorisation autant que possible avant de procéder à une fusion réelle.

L'outil de fusion est un peu idiot, car il ne tient compte que des différences textuelles et ne comprend en aucune manière le code. Si la branche principale a modifié le nom d'une classe utilisée dans l'application et que la branche utilise l'ancien nom dans un nouveau code, l'outil de fusion ne comprendra pas que le nom de la classe doit également être modifié dans le nouveau code. Mais si vous effectuez un refactoring dans la branche B pour renommer la classe comme dans la branche A, cela fonctionnera à la fois dans l'ancien et le nouveau code et la fusion se déroulera sans heurts.

Deuxièmement, vous devez vérifier la localisation des modifications dans la branche de développement. Si les modifications apportées à la branche de fonctionnalités sont localisées dans quelques zones, vous n'avez pas à faire converger le code non affecté, vous pouvez simplement copier et écraser à partir de la branche principale.

Dans les zones de code où des modifications non triviales ont eu lieu sur les deux branches, vous devez inspecter soigneusement le code et décider de la procédure de réécriture.

JacquesB
la source