web-dev-qa-db-fra.com

Git fusionner avec écrasement forcé

J'ai une branche appelée demo que je dois fusionner avec master branche. Je peux obtenir le résultat souhaité avec les commandes suivantes:

git pull Origin demo
git checkout master
git pull Origin master
git merge demo
git Push Origin master

Mon seul souci est, s'il y a des problèmes de fusion, je veux dire à git d'écraser les modifications dans la branche master sans me donner l'invite de fusion. Donc, fondamentalement, les changements dans la branche demo devraient écraser automatiquement ceux de la branche master.

J'ai regardé autour de moi, il y a plusieurs options mais je ne veux pas prendre de risques avec la fusion.

62
OpenStack

Pas vraiment lié à cette réponse, mais j'aurais abandonné git pull, qui ne fait que lancer git fetch suivi de git merge. Vous faites trois fusions, ce qui obligera votre Git à exécuter trois opérations d'extraction, lorsqu'une extraction est tout ce dont vous aurez besoin. Par conséquent:

git fetch Origin   # update all our Origin/* remote-tracking branches

git checkout demo         # if needed -- your example assumes you're on it
git merge Origin/demo     # if needed -- see below

git checkout master
git merge Origin/master

git merge -X theirs demo   # but see below

git Push Origin master     # again, see below

Contrôler la fusion la plus délicate

La partie la plus intéressante ici est git merge -X theirs. Comme noté root545 , les options -X sont transférées à la stratégie de fusion, et la stratégie par défaut recursive et la stratégie alternative resolve prennent -X ours ou -X theirs (l'une ou l'autre). , mais pas les deux). Pour comprendre ce qu’ils font, vous devez savoir comment Git trouve et traite les conflits de fusion .

Un conflit de fusion peut survenir dans certains fichiers.1 lorsque la version de base diffère de la version actuelle (également appelée locale, HEAD ou --ours) version et l'autre version (également appelée distante ou --theirs) de ce même fichier. C'est-à-dire que la fusion a identifié trois révisions (trois commits): base, la nôtre et la leur. La version "base" provient de la base de fusion entre notre commit et leur commit, comme indiqué dans le graphe de commit (pour plus d'informations à ce sujet, voir autre Messages StackOverflow). Git a ensuite trouvé deux ensembles de modifications: "ce que nous avons fait" et "ce qu'ils ont fait". Ces modifications se trouvent (en général) ligne par ligne, purement textuelles . Git ne comprend pas vraiment le contenu du fichier. c'est simplement comparer chaque ligne de texte.

Ces modifications correspondent à ce que vous voyez dans la sortie de git diff et, comme toujours, elles ont également un contexte . Il est possible que les choses que nous avons changées soient sur des lignes différentes de celles qu'ils ont changées, de sorte que les modifications ne semblent pas entrer en collision, mais le contexte a également changé (par exemple, notre modification étant proche du haut ou du bas du fichier, de sorte que le fichier s’épuise dans notre version, mais dans la leur, ils ont également ajouté plus de texte en haut ou en bas).

Si les modifications se produisent sur différentes lignes , par exemple, nous changeons color en colour à la ligne 17 et elles changent fred à barney à la ligne 71 - il n'y a alors aucun conflit: Git prend simplement les deux modifications . Si les changements se produisent sur les mêmes lignes, mais sont identiques , Git prend une copie du changement. Vous obtenez un conflit de modification/modification uniquement si les modifications sont sur les mêmes lignes, mais qu'il s'agisse de modifications différentes ou de ce cas particulier de contexte interférant.

Les options -X ours et -X theirs indiquent à Git comment résoudre ce conflit en sélectionnant l'un des deux changements: le nôtre ou le leur. Puisque vous avez dit que vous fusionniez demo (le leur) en master (le nôtre) et que vous souhaitiez les modifications apportées à demo, vous souhaiteriez le -X theirs.

L'application aveugle de -X est cependant dangereuse. Le simple fait que nos modifications n'entrent pas en conflit ligne par ligne ne signifie pas que nos modifications ne sont pas en conflit! Un exemple classique se produit dans les langues avec des déclarations de variables. La version de base peut déclarer une variable inutilisée:

int i;

Dans notre version, nous supprimons la variable inutilisée pour faire disparaître un avertissement du compilateur - et dans leur version , ils ajoutent une boucle quelques lignes plus tard, en utilisant i comme compteur de boucle. Si nous combinons les deux modifications, le code résultant ne sera plus compilé. L'option -X n'est d'aucune aide ici car les modifications sont sur des lignes différentes .

Si vous avez une suite de tests automatisée, le plus important est d'exécuter les tests après la fusion. Vous pouvez le faire après vous être engagé et régler le problème plus tard si nécessaire. ou vous pouvez le faire avant de commettre, en ajoutant --no-commit à la commande git merge. Nous laisserons les détails de tout cela à d'autres publications.


1Vous pouvez également avoir des conflits en ce qui concerne les opérations "au niveau du fichier", par exemple, nous corrigeons peut-être l'orthographe d'un mot dans un fichier (afin que nous ayons une modification), et ils delete l'intégralité du fichier (afin qu'ils aient une suppression). Git ne résoudra pas seul ces conflits, quels que soient les arguments -X.


Faire moins de fusions et/ou des fusions plus intelligentes et/ou en utilisant rebase

Il y a trois fusions dans nos deux séquences de commandes. La première consiste à introduire Origin/demo dans le local demo (le vôtre utilise git pull qui, si votre Git est très ancien, ne pourra pas mettre à jour Origin/demo mais produira le même résultat final). La seconde consiste à introduire Origin/master dans master.

Il m'est difficile de savoir qui met à jour demo et/ou master. Si vous écrivez votre propre code sur votre propre branche demo, et écrivez du code et le transmettez à la branche demo sur Origin, alors cette fusion de première étape peut avoir des conflits ou produire une fusion réelle. Plus souvent qu'autrement, il est préférable d'utiliser rebase plutôt que de fusionner pour combiner travail (certes, c'est une question de goût et d'opinion). Si tel est le cas, vous pouvez utiliser plutôt git rebase. Par contre, si vous ne faites jamais vos propres commits sur demo, vous n'avez même pas besoin de a demo branche. Alternativement, si vous voulez automatiser beaucoup de choses, mais que vous pouvez vérifier soigneusement quand il y a des commits que vous et d'autres faites, vous voudrez peut-être utiliser git merge --ff-only Origin/demo: ceci fera avancer rapidement votre demo pour correspondre le Origin/demo mis à jour, si possible, et échouez carrément si ce n'est pas le cas (vous pouvez alors inspecter les deux ensembles de modifications et choisir une fusion ou une base réelle, selon le cas).

Cette même logique s'applique à master, bien que vous fassiez la fusion sur master, vous avez donc absolument besoin d'un master. Cependant, il est même plus probable que vous souhaitiez que la fusion échoue si elle ne peut pas être effectuée en tant que non-fusion rapide, ce qui devrait donc également être git merge --ff-only Origin/master.

Disons que vous ne faites jamais vos propres commits sur demo. Dans ce cas, nous pouvons supprimer le nom demo:

git fetch Origin   # update Origin/*

git checkout master
git merge --ff-only Origin/master || die "cannot fast-forward our master"

git merge -X theirs Origin/demo || die "complex merge conflict"

git Push Origin master

Si vous effectuez votre propre commise de branche demo, ceci n'est pas utile; vous pouvez aussi bien conserver la fusion existante (mais peut-être ajouter --ff-only en fonction du comportement souhaité) ou la changer pour une refonte. Notez que les trois méthodes peuvent échouer: la fusion peut échouer avec un conflit, la fusion avec --ff-only peut ne pas être en mesure d'avancer rapidement et la rebase peut échouer avec un conflit (la refonte fonctionne essentiellement, la sélection des commandes est validée, elle utilise le mécanisme de fusion et peut donc donner lieu à un conflit de fusion).

71
torek

Vous pouvez essayer "la nôtre" dans la fusion de git,

branche de fusion git -X notre

Cette option oblige les mecs en conflit à se résoudre eux-mêmes automatiquement en privilégiant notre version. Les modifications de l’autre arbre qui ne sont pas en conflit avec notre côté se reflètent dans le résultat de la fusion. Pour un fichier binaire, tout le contenu est pris de notre côté.

9
manuj

J'avais un problème similaire, je devais remplacer efficacement tout fichier comportant des modifications/des conflits avec une branche différente.

La solution que j'ai trouvée consistait à utiliser git merge -s ours branch.

Notez que l'option est -s et non pas -X. -s indique l'utilisation de ours comme stratégie de fusion de niveau supérieur, -X appliquerait l'option ours à la stratégie de fusion recursive, ce qui n'est pas ce que Je (ou nous) voulons dans ce cas.

Étapes, où oldbranch est la branche que vous souhaitez écraser avec newbranch.

  • git checkout newbranch vérifie la branche que vous souhaitez conserver
  • git merge -s ours oldbranch fusionne dans l'ancienne branche, mais conserve tous nos fichiers.
  • git checkout oldbranch vérifie la branche que vous voulez écraser
  • get merge newbranch fusionne dans la nouvelle branche et remplace l'ancienne.
5
Niall Mccormack