Branches et Merges Avancés

Trouver de l'aide pour résoudre les conflits durant un merge

Tous les changements que git peut merger automatiquement sont déjà ajoutés à l'index, donc git diff ne vous montre que les conflits. Il a une syntaxe peu commune:

$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,5 @@@
++<<<<<<< HEAD:file.txt
 +Hello world
++=======
+ Goodbye
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

Souvenez-vous que le commit qui sera effectué après que nous ayons résolu ce conflit aura 2 parents: l'un sera le sommet de la branche courante (HEAD) et l'autre sera le sommet de la branche qui s'occupe du merge, stockée temporairement dans MERGE_HEAD.

Durant le merge, l'index garde 3 versions de chaque fichiers. Une de ces 3 "étape de fichier" représente une version différente du fichier:

$ git show :1:fichier.txt   # Le fichier dans l'ancêtre commun des 2 branches
$ git show :2:fichier.txt   # La version présente dans HEAD.
$ git show :3:fichier.txt   # La version présente dans MERGE_HEAD.

Quand vous demandez à git diff de vous montrer les conflits, il fait une différence en 3 points entre les résultats conflictuels de merge dans le répertoire de travail avec les version 2 et 3 pour montrer seulement les morceaux de code qui ont du contenu de chaque côté, mélangés (en d'autres termes, quand un morceau du résultat du merge ne vient que de la version 2, alors ce morceau n'est pas en conflit est n'est pas affiché. Idem pour la version 3).

La différence en début de chapitre vous montre les différences entre la version de travail de fichier.txt et les versions 2 et 3. Donc au lieux de rajouter les préfixes "+" ou "-" devant chaque ligne, on utilise maintenant 2 colonnes pour ces préfixes: la première colonne est utilisée pour les différences entre le premier parent et la copie du répertoire de travail, et la deuxième pour les différences entre le second parent et la copie du répertoire de travail. (Voir la section "COMBINED DIFF FORMAT" dans la documentation de git diff-files pour plus de détails sur ce format.)

Après avoir résolu le conflit de manière évidente (mais avant de mettre à jour l'index), le diff ressemblera à:

$ git diff
diff --cc file.txt
index 802992c,2b60207..0000000
--- a/file.txt
+++ b/file.txt
@@@ -1,1 -1,1 +1,1 @@@
- Hello world
-Goodbye
++Goodbye world

Cela montre que notre version résolue a effacé "Hello world" du premier parent, et effacé "Goodbye" du second parent, puis ajouté "Goodbye world", qui était avant absent des 2.

Quelques options spéciales de diff permettent de faire la différence entre le répertoire de travail et les différentes étapes:

$ git diff -1 file.txt          # différence avec l'étape 1
$ git diff --base file.txt      # même chose que ci-dessus
$ git diff -2 file.txt          # différence avec l'étape 2
$ git diff --ours file.txt      # même chose que ci-dessus
$ git diff -3 file.txt          # différence avec l'étape 3
$ git diff --theirs file.txt    # même chose que ci-dessus

Les commandes git log and gitk fournissent aussi de l'aide particulière pour les merges:

$ git log --merge
$ gitk --merge

Cela vous montrera tous les commits qui existent seulement des HEAD ou dans MERGE_HEAD, et qui concernant les fichiers non-mergés.

Vous pouvez aussi utiliser git mergetool, qui vous permet de merger des fichiers non-mergés en utilisant des outils externes comme emacs ou kdiff3.

Chaque fois que vous résolvez les conflits d'un fichier et que vous mettez à jour l'index:

$ git add fichier.txt

les différentes étapes de se fichiers "s'effondreront", après quoi git-diff ne montrera plus (par défaut) de différence pour ce fichier.

Merge Multiples

Vous pouvez combiner plusieurs branches en même temps en les listant simplement dans la même commande git merge. Par exemple:

$ git merge scott/master rick/master tom/master

est l'équivalent de:

$ git merge scott/master
$ git merge rick/master
$ git merge tom/master

Subtree

Il y a des moments où vous voulez inclure le contenu d'un projet développé indépendamment dans votre projet. Vous pouvez juste récupérer le code de cet autre projet tant que les chemins ne rentrent pas en conflit.

Les problèmes surviennent quand il y a des fichiers en conflit. Les candidats potentiels sont les Makefiles et les autres noms de fichiers standardisés. Vous pouvez merger ces fichiers, mais ce n'est pas forcément ce que vous voulez faire. Une meilleure solution pour ce problème peut être de merger le projet dans un sous-répertoire. Mais la stratégie de merge ne supporte la récursivité, donc la simple récupération des fichiers ne fonctionnera pas.

Vous voudrez alors utiliser la stratégie de merge subtree, qui vous aidera dans cette situation.

Dans cet exemple, disons que vous avez un dépôt dans /path/to/B (si vous voulez ça peut aussi être une adresse URL). Vous voulez merger la branche "master" de ce dépôt dans le sous-répertoire dir-B de la branche actuelle.

Voici la séquence de commande que vous utiliserez:

$ git remote add -f Bproject /path/to/B (1)
$ git merge -s ours --no-commit Bproject/master (2)
$ git read-tree --prefix=dir-B/ -u Bproject/master (3)
$ git commit -m "Merge B project as our subdirectory" (4)
$ git pull -s subtree Bproject master (5)

Utiliser le merge subtree vous permet d'apporter moins de complications administratives aux les utilisateurs de votre dépôt. Elle est aussi compatible avec des version plus anciennes de git (jusqu'à Git v1.5.2) et vous aurez le code juste après le clonage.

Cependant, si vous utilisez des sous-modules, vous pouvez alors choisir de ne pas transférer les objets de ces sous-modules. Cela peut poser un problème lors de l'utilisation des merge subtree.

Il est aussi plus facile d'effectuer des changement dans votre autre projet si vous utiliser les sous-modules.

(from Using Subtree Merge)



github logo