Comment aborder une fusion compliquée

25

Voici l'affaire, j'ai rejoint une nouvelle entreprise et on m'a demandé de terminer le travail sur une branche qui n'a pas été touchée depuis près d'un an. Entre-temps, la branche maître progresse à un rythme soutenu. Idéalement, je voudrais fusionner toutes les modifications de la branche principale dans la branche des fonctionnalités et continuer le travail à partir de là, mais je ne sais pas trop comment aborder cela.

Comment effectuer cette fusion en toute sécurité tout en préservant les modifications importantes des deux côtés de la branche?

Vlad Spreys
la source
Merci à tous pour vos commentaires impressionnants. Je vais essayer git-imerge et si cela s'avère désordonné, j'utiliserai une nouvelle approche de branche!
Vlad Spreys
Quelqu'un peut-il expliquer pourquoi nous ne pouvons pas utiliser git cherry-pickici?
Santosh Kumar
1. Prières. 2. rebaser. 3. test. 4. fusionner.
AK_
1
Je préfère le rebasage dans cette situation car elle se fera commit par commit. Il vous permettra également d'écraser la nouvelle fonctionnalité avant de rendre disponible le code fusionné. YMMV.
Stephen

Réponses:

27

Au fond, comment combiner deux morceaux de code (éventuellement non compatibles) est un problème de développement , pas un problème de contrôle de version . La commande Git merge peut aider dans ce processus, mais cela dépend de la forme du problème.

La comparaison des deux versions avec la base est la plus logique. Cela vous donnera une idée de la meilleure stratégie pour aller de l'avant. Votre approche peut être différente en fonction de la nature et du chevauchement des changements dans chaque branche.

Imaginez le scénario idéal: vous découvririez que la branche principale et la branche de fonctionnalité ne modifient chacune que des parties mutuellement exclusives du code, de sorte que vous pouvez simplement valider toutes les modifications et être prêt à partir.

Bien sûr, ce ne sera certainement pas le cas, mais la question est: à quelle distance de ce scénario idéal sera-t-il? c'est-à-dire à quel point les changements sont-ils entremêlés?

En outre, quelle était la maturité de l'ancienne branche de fonctionnalité? Était-il en bon état de fonctionnement ou non (ou inconnu)? Quelle partie de la fonctionnalité a été terminée?

Si le code pertinent dans la branche principale a beaucoup changé au cours de la dernière année, ou si la fonctionnalité n'est pas dans un état très mature, je pourrais envisager de créer une nouvelle fourche de la dernière et d'incorporer manuellement l'ancienne fonctionnalité à nouveau. Cela vous permettra d'adopter une approche progressive pour le faire fonctionner.

Si vous faites une fusion désordonnée de beaucoup de code et que cela ne fonctionne pas, il sera assez difficile à déboguer. Si la branche principale a beaucoup changé au cours de la dernière année, des modifications majeures de la conception peuvent être nécessaires pour la fonctionnalité afin de la faire fonctionner. Il ne serait pas approprié d'apporter ces modifications via «résoudre les conflits», car cela nécessiterait d'effectuer toutes les modifications en même temps et en espérant que cela fonctionne. Ce problème serait aggravé par la possibilité de bogues dans l'ancienne branche partiellement terminée.


la source
+1 "Je pourrais envisager de créer une nouvelle fourchette de la dernière et d'incorporer manuellement l'ancienne fonctionnalité à nouveau"
mika
1
La première question clé est: est-ce que l'ancienne branche de fonctionnalité se construit? Fonctionne-t-il? Sinon, il sera très difficile de tester votre fusion.
Móż
22

Dans mon expérience limitée de Git, je peux dire qu'il est parfois plus rapide de redémarrer la branche de fonctionnalité si le maître est allé trop loin du point de détachement.

Fusionner deux branches sans connaître l'historique du code (étant donné que vous venez de rejoindre le projet) est vraiment difficile, et je parie que même un développeur qui a suivi le projet depuis le début commettrait probablement des erreurs lors de la fusion.

Bien sûr, cela a du sens si la branche de fonctionnalité n'est pas énorme, mais vous pouvez simplement garder l'ancienne branche de fonctionnalité ouverte, la ramener à partir du maître et réintroduire manuellement les modifications qui composent cette fonctionnalité. Je sais que c'est l'approche la plus manuelle, mais elle vous permet d'avoir un contrôle total en cas de code manquant ou déplacé.

La programmation en binôme avec un senior dans ce cas serait le meilleur des cas, vous aidant à mieux connaître le code.
Cela pourrait même s'avérer plus rapide aussi, si vous prenez en compte les conflits de fusion et le temps de test!

J'ai en quelque sorte supposé qu'au moins essayer de faire une fusion est évidemment la meilleure chose à faire. Si cela échoue ou s'avère trop difficile, essayez la cueillette des cerises, si cela ne fonctionne pas, suivez la méthode manuelle.

GavinoGrifoni
la source
2
Certainement pas la bonne approche.
Andy
12
non, c'est la bonne approche - si vous avez une ancienne branche, et que vous ne connaissez pas le code, essayer de fusionner ne va pas être très réussi et cela va être très risqué. Déterminer les modifications qui ont été apportées à l'origine, rechercher si elles sont même pertinentes pour le nouveau code, puis les appliquer pour donner un sens est la façon de le faire. Il s'agit d'une approche manuelle, mais dans ces circonstances, la seule à prendre en toute sécurité. Bien sûr, j'essaierais toujours une fusion d'abord, juste pour voir ce qui se passe, et je vérifierais le journal pour voir à quel point les changements se sont produits sur la branche - cela pourrait être trivial.
gbjbaanb
10
@DavidPacker: Je ne pense pas que GavianoGrifoni suggère de jeter tout le travail par dessus bord. Il suggère de transférer les changements de l'ancienne branche manuellement vers la ligne de développement actuelle, de manière progressive. Cela jettera la vieille histoire , pas plus.
Doc Brown
3
@DavidPacker 1er: la branche est périmée depuis un an, 2ème: le gars chargé de la terminer ne connaît pas du tout le code. Compte tenu de ces 2 facteurs, une nouvelle demande manuelle est la seule façon réaliste d'aborder la tâche. Personne ne propose un simple copier-coller de la révision de pointe de l'ancienne branche.
gbjbaanb
5
@DavidPacker: Les conflits de fusion peuvent devenir mauvais - si vous devez en résoudre 500 à la fois avant de remettre le programme dans un état compilable et testable. C'est le genre de situation que le PO attend ici. Si vous pensez qu'il est possible d'utiliser git de manière efficace pour éviter cette situation "tout ou rien", pourquoi ne modifiez-vous pas votre réponse et ne dites-vous pas au PO comment cela peut être accompli?
Doc Brown du
16

git-imerge est conçu exactement à cet effet. Il s'agit d'un outil git qui fournit une méthode de fusion incrémentielle . En fusionnant de manière incrémentielle, vous n'avez qu'à gérer les collisions entre deux versions, jamais plus. En outre, un nombre beaucoup plus important de fusions peut être effectué automatiquement car les ensembles de modifications individuels sont plus petits.

Joe
la source
6

Essayer de fusionner la tête principale sur une branche périmée de l'année peut être un exercice de frustrations et d'approfondir la bosselure sur le bureau avec votre front.

La ligne principale n'est pas arrivée à sa place en une seule fois au cours des mois. Il avait également du développement et des versions. Essayer de tout mettre à jour en une seule fusion monolithique peut être écrasant.

Au lieu de cela, commencez par fusionner de la première fonctionnalité à la fusion avec la ligne principale après la division de la branche périmée. Faites fonctionner cette fusion. Ensuite, la fonctionnalité suivante fusionne. Etc. Beaucoup de ces fonctionnalités fusionneront sans conflit. Il est toujours important de s'assurer que la fonctionnalité actuelle de la branche périmée reste compatible avec la direction dans laquelle la ligne principale a disparu.

Vous souhaiterez peut-être dériver du chef de la branche périmée pour le rôle de la fusion dans d'autres changements. Il s'agit davantage de s'assurer que les validations et l'historique lorsque quelqu'un y revient sont clairs et communiquent quel est le rôle et la politique de chaque branche. La branche périmée était une branche caractéristique. Celui à partir duquel vous travaillez est une branche d'accumulation et de réconciliation.

Une grande partie de cela sera plus facile si les anciennes branches de fonctionnalités ou de versions existent toujours et sont facilement accessibles (certains endroits ont une politique de nettoyage des noms des branches plus anciennes que certaines dates afin que la liste des branches ne soit pas écrasante ).

L'important dans tout cela est de s'assurer que vous testez et corrigez après avoir fusionné avec succès chaque partie de l'historique de la ligne principale. Même si quelque chose peut fusionner sans conflits, cela signifie simplement que le code n'a pas été en conflit. Si le mode d'accès à la fonction périmée a été déprécié ou supprimé, des correctifs peuvent être nécessaires après la fusion réussie.

En passant, cela fonctionne également pour d'autres systèmes de contrôle de version. J'ai parfois eu besoin de fusionner un groupe spécifique de commits svn dans une branche (sélection de cerises) pour une fonctionnalité, de corriger la branche pour qu'elle fonctionne avec cette fonctionnalité, puis de fusionner le groupe de commits svn suivant plutôt que de simplement faire un svn en gros fusionner.

Bien que l'on puisse faire un choix de cerise sur le gâteau ici, et qu'il permette d'apporter des commits spécifiques , cela présente certains inconvénients qui peuvent compliquer le processus. La cerise sur le gâteau n'affichera pas d'informations sur le commit que vous avez choisi (vous pouvez l'ajouter au message de commit). Cela rend plus difficile le suivi des commits dans l'histoire.

En outre, cela signifie que vous n'allez pas rejouer efficacement le maître sur la branche périmée - vous allez choisir des fonctionnalités éventuellement incomplètes - et ces fonctionnalités peuvent être lues dans le désordre.

La principale raison pour laquelle il faut fusionner des validations historiques à maîtriser sur la branche périmée est de pouvoir conserver le, appelons-le "histoire future" de la branche périmée dans un état sur lequel vous pouvez raisonner. Vous pouvez clairement voir les fusions de l'historique sur la branche périmée et les correctifs pour réintégrer la fonctionnalité. Les fonctionnalités sont ajoutées dans le même ordre qu'elles devaient être maîtrisées. Et quand vous avez terminé, et enfin faites la fusion du chef de master sur la branche périmée, vous savez que tout a été fusionné et vous ne manquez aucun commit.


la source
+1 Il s'agit d'une solution alternative potentielle intéressante qui utilise la fusion pour incorporer des modifications importantes. Cependant, je peux voir un inconvénient: supposons que vous ayez des versions majeures ABCDE et que vous souhaitiez incorporer la branche de fonctionnalité A1 dans E. Il peut y avoir beaucoup d'efforts inutiles pour fusionner le code en BC et D. Par exemple, que se passerait-il si D à E était un gros changement de conception qui a rendu les changements incrémentiels de B, C et D non pertinents? En outre, cela dépend encore une fois de la maturité de la fonctionnalité en premier lieu. Une approche potentielle utile, mais sa pertinence doit être considérée avant de commencer.
1

Étape 1. Découvrez le code, analysez son architecture et les modifications apportées aux deux branches depuis le dernier ancêtre commun.

Étape 2. Si la fonctionnalité semble largement indépendante et touche principalement différents domaines de code, fusionnez, résolvez les conflits, testez, corrigez, etc. Sinon, passez à l'étape 3

Étape 3. Analysez les zones de conflit, comprenez l'impact fonctionnel et les raisons en détail. Il pourrait facilement y avoir des conflits dans les exigences commerciales qui apparaissent ici. Discutez avec les BA, les autres développeurs, le cas échéant. Découvrez la complexité de la résolution de l'interférence.

Étape 4. À la lumière de ce qui précède, décidez si vous souhaitez fusionner / sélectionner / même couper-coller uniquement les parties qui n'entrent pas en conflit et réécrire les pièces en conflit, OU si vous devez réécrire toute la fonctionnalité à partir de zéro .

Brad Thomas
la source
0

1. Basculez vers la branche qui est utilisée comme principale branche développeur / version.

Il s'agit de la branche qui contient les dernières modifications apportées au système. Peut - être master, core, dev, cela dépend de la société. Dans votre cas, c'est probablement masterdirectement.

git checkout master
git pull

Tirez pour vous assurer que vous avez acquis la dernière version de la branche principale de développement.

2. Vérifiez et tirez la branche qui contient le travail que vous êtes censé terminer.

Vous tirez pour vous assurer que vous disposez bien du dernier contenu de la branche. En le vérifiant directement, sans le créer localement au préalable, vous vous assurez de ne pas y avoir le nouveau contenu master(ou la branche principale de développement respectivement).

git checkout <name of the obsolete branch>
git pull origin <name of the obsolete branch>

3. Fusionnez la branche principale de développement à la branche obsolète.

Avant d'exécuter la commande suivante, assurez-vous, soit en tapant, git branchsoit git statusque vous êtes sur la branche obsolète.

git merge master

La git mergecommande essaiera de fusionner le contenu de la branche spécifiée, dans ce cas master, avec la branche dans laquelle vous vous trouvez actuellement.

L'accent sera mis sur . Il peut y avoir des conflits de fusion, qui devront être résolus par vous et par vous seul.

4. Corrigez les conflits de fusion, validez et poussez le correctif de conflit

Après avoir corrigé le conflit de fusion dans tous les fichiers où il se trouve, mettez en scène, validez et poussez la résolution du conflit vers origin.

git add .
git commit -m "fixed the merge conflict from the past year to update the branch"
git push

Vous pouvez généralement appeler git add .pour mettre en scène tous les fichiers à valider. Lorsque vous traitez des conflits de fusion, vous souhaitez que tous les fichiers nécessaires soient mis à jour.

Remarque additionnelle

La résolution des conflits de fusion peut être un travail fastidieux. Surtout si vous êtes nouveau dans une entreprise. Vous n'avez peut-être même pas encore les connaissances nécessaires pour résoudre tous les conflits de fusion.

Prenez le temps d'inspecter attentivement tous les conflits qui se sont produits et de les corriger de manière appropriée, avant de poursuivre votre travail.


Cela peut arriver, vous commencez à travailler sur une branche vieille d'un an, vous y fusionnez l'état de développement actuel et vous n'aurez aucun conflit de fusion.

Cela se produit lorsque même si le système a beaucoup changé au cours de l'année, personne n'a touché aux fichiers qui ont été modifiés dans la branche d'un an.

Andy
la source
6
# 4 est potentiellement le problème. S'il y a eu beaucoup de changements au cours de l'année dernière, des changements majeurs à l'ancienne fonctionnalité pourraient être nécessaires. Pour ce faire, par fusion, vous devez apporter des modifications majeures au code en une seule fois et espérer que cela fonctionne, ce qui n'est pas une bonne pratique de développement. De plus, qui sait quel était l'état de la fonction inachevée? Vous pouvez vous retrouver avec un énorme gâchis de code non fonctionnel, et qui sait si cela était dû à des problèmes avec l'original ou à des modifications que vous avez apportées?
David, tant que cette approche standard fonctionne, tout va bien, et le PO devrait essayer ceci en premier. Mais il y a certainement un risque d'avoir trop de conflits de fusion dans la situation décrite pour les gérer de cette manière «tout ou rien».
Doc Brown
@ dan1111 Qu'il y ait des conflits de fusion est tout à fait OK, et en fait les traverser est la voie à suivre. Étant donné que la succursale n'a pas été touchée pendant un an, vous pouvez être sûr que ce n'est rien d'important et n'affectera pas beaucoup le système. Ainsi, même si la branche a un an de retard, vous pouvez obtenir aussi peu que 2 à aucun conflit de fusion.
Andy
4
L'hypothèse selon laquelle cette branche n'est pas importante est injustifiée. Cela aurait pu être un changement de conception fondamental qui a été abandonné et qui est maintenant repris. Ça pourrait être n'importe quoi. Vous avez raison, cela pourrait être une question simple et il pourrait y avoir peu ou pas de conflits - auquel cas votre réponse serait correcte. Mais ce n'est pas la seule possibilité.
@ dan1111 Si quelqu'un n'a pas touché un emplacement de fonctionnalité dans une branche distincte pendant un an, cela ne va pas autant réfléchir aux changements du système. Cela vient de ma propre expérience avec les branches obsolètes (6+ mois).
Andy