Utiliser des branches

Conflits de merge Git

Les systèmes de contrôle de version sont, par essence, destinés à la gestion des contributions entre plusieurs auteurs distribués (généralement, des développeurs). Parfois, plusieurs développeurs peuvent essayer de modifier le même contenu. Si le développeur A essaie de modifier le même code que le développeur B, un conflit peut survenir. Pour limiter l'apparition de conflits, les développeurs doivent travailler dans des branches isolées et distinctes. La commande git merge a pour objectif premier de combiner des branches distinctes et de résoudre les conflits d'édition.

Comprendre les conflits de merge

Les merges et les conflits font partie intégrante de l'expérience Git. Les conflits dans d'autres outils de contrôle de version comme SVN peuvent s'avérer coûteux et chronophages. Git rend le merge super facile. La plupart du temps, Git trouvera comment intégrer automatiquement les nouveaux changements.

Les conflits surviennent généralement lorsque deux personnes ont modifié les mêmes lignes dans un fichier, ou si un développeur a supprimé un fichier alors qu'un autre développeur le modifiait. Dans ces cas, Git ne peut pas déterminer automatiquement la version correcte. Les conflits n'affectent que le développeur qui effectue le merge, les autres membres de l'équipe ne sont pas conscients du conflit. Git marquera le fichier comme étant en conflit et arrêtera le processus de merge. Il incombe alors aux développeurs de résoudre le conflit.

Types de conflits de merge

Un merge peut entrer dans un état de conflit en deux points distincts. Au début d'un processus de merge et au cours de celui-ci. Dans la discussion qui suit, nous verrons comment aborder chacun de ces scénarios de conflit.

Git ne parvient pas à lancer le merge

Le démarrage du merge échouera tant que Git verra qu'il y a des changements dans le répertoire de travail ou la zone de staging du projet en cours. Git ne parvient pas à lancer le merge, parce que ces changements en suspens pourraient être remplacés par les commits mergés. Le cas échéant, ce n'est pas dû à des conflits avec d'autres développeurs, mais bien avec des changements locaux en cours. L'état local devra être stabilisé grâce aux commandes git stash, git checkout, git commit ou git reset. L'échec d'un merge au démarrage renverra le message d'erreur suivant :

error: Entry '<fileName>' not uptodate. Cannot merge. (Changes in working directory)

Git rencontre un problème durant le merge

Un échec PENDANT un merge indique un conflit entre la branche locale actuelle et la branche mergée. Cela indique un conflit avec le code d'autres développeurs. Git fera de son mieux pour merger les fichiers, mais vous laissera le soin de résoudre manuellement les conflits dans les fichiers concernés. Un échec en cours de merge renverra le message d'erreur suivant :

error: Entry '<fileName>' would be overwritten by merge. Cannot merge. (Changes in staging area)

Création d'un conflit de merge

Afin de vous familiariser réellement avec les conflits de merge, la section suivante simulera un conflit à examiner et à résoudre ultérieurement. L'exemple utilisera une interface Git de ligne de commande de type Unix pour exécuter la simulation.

$ mkdir git-merge-test
$ cd git-merge-test
$ git init .
$ echo "this is some content to mess with" > merge.txt
$ git add merge.txt
$ git commit -am"we are commiting the inital content"
[master (root-commit) d48e74c] we are commiting the inital content
1 file changed, 1 insertion(+)
create mode 100644 merge.txt

Cet exemple de code exécute une séquence de commandes qui permettent d'obtenir les résultats suivants.

  • Créer un répertoire nommé git-merge-test, basculer vers ce répertoire, puis l'initialiser en tant que nouveau dépôt Git.
  • Créer un fichier texte merge.txt avec du contenu.
  • Ajouter merge.txt au dépôt et le commiter.

Nous disposons à présent d'un nouveau dépôt avec une branche principale (master) et un fichier merge.txt avec du contenu. Nous allons maintenant créer une branche à utiliser en tant que merge conflictuel.

$ git checkout -b new_branch_to_merge_later
$ echo "totally different content to merge later" > merge.txt
$ git commit -am"edited the content of merge.txt to cause a conflict"
[new_branch_to_merge_later 6282319] edited the content of merge.txt to cause a conflict
1 file changed, 1 insertion(+), 1 deletion(-)

La séquence de commandes est la suivante :

  • Créer une branche nommée new_branch_to_merge_later et en faire un check-out
  • Remplacer le contenu dans le fichier merge.txt
  • Faire un commit du nouveau contenu

Avec cette nouvelle branche : new_branch_to_merge_later, nous avons créé un commit qui remplace le contenu du fichier merge.txt

git checkout master
Switched to branch 'master'
echo "content to append" >> merge.txt
git commit -am"appended content to merge.txt"
[master 24fbe3c] appended content to merge.tx
1 file changed, 1 insertion(+)

Cette chaîne de commandes fait un check-out de la branche principale (master), ajoute du contenu au fichier merge.txt et le commite. Notre exemple de dépôt se retrouve ainsi à un état où nous avons deux nouveaux commits. Un dans la branche principale (master) et un dans la branche new_branch_to_merge_later. À présent, exécutons la commande git merge new_branch_to_merge_later pour voir ce qui se produit !

$ git merge new_branch_to_merge_later
Auto-merging merge.txt
CONFLICT (content): Merge conflict in merge.txt
Automatic merge failed; fix conflicts and then commit the result.

BOUM 💥. Un conflit apparaît. Et Git est tellement sympa qu'il nous en avertit !

Comment identifier les conflits de merge ?

Comme nous l'avons vu dans l'exemple de la procédure, Git générera une sortie descriptive nous permettant de savoir qu'un CONFLIT s'est produit. Pour en savoir plus, nous pouvons exécuter la commande git status.

$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)

Unmerged paths:
(use "git add <file>..." to mark resolution)

both modified:   merge.txt

La sortie de la commande git status indique que des chemins n'ont pas été mergés en raison d'un conflit. Le fichier merge.text apparaît désormais à un état modifié. Examinons le fichier et découvrons ce qui a été modifié.

$ cat merge.txt
<<<<<<< HEAD
this is some content to mess with
content to append
=======
totally different content to merge later
>>>>>>> new_branch_to_merge_later

Ici, nous avons utilisé la commande cat pour extraire le contenu du fichier merge.txt . Nous pouvons voir certains ajouts étranges.

  • <<<<<<< HEAD
  • =======
  • >>>>>>> new_branch_to_merge_later

Considérez ces nouvelles lignes comme des « séparateurs de conflit ». La ligne ======= est le « centre » du conflit. Tout le contenu entre le centre et la ligne <<<<<<< HEAD dans la branche principale actuelle vers laquelle pointe la réf HEAD. Autrement, tout le contenu entre le centre et >>>>>>> new_branch_to_merge_later est présent dans notre branche de merge.

Comment résoudre des conflits de merge à l'aide de la ligne de commande ?

La manière la plus directe de résoudre un conflit de merge consiste à modifier le fichier conflictuel. Ouvrez le fichier merge.txt dans votre éditeur préféré. Dans notre exemple, supprimons simplement tous les séparateurs de conflit. Le contenu du fichier merge.txt modifié devrait ressembler à ceci :

this is some content to mess with
content to append
totally different content to merge later

Une fois le fichier modifié, utilisez git add merge.txt pour stager le nouveau contenu mergé. Pour finaliser le merge, créez un commit en exécutant :

git commit -m "merged and resolved the conflict in merge.txt"

Git voit que le conflit a été résolu et crée un commit de merge pour finaliser le merge.

Les commandes Git qui peuvent aider à résoudre des conflits de merge

Outils généraux

git status

La commande status est fréquemment utilisée pour travailler avec Git, et elle permet d'identifier les fichiers en conflit durant un merge.

git log --merge

La transmission de l'argument --merge à la commande git log génère un journal contenant une liste de commits en conflit entre les branches de merge.

git diff

diff aide à trouver les différences entre les états d'un dépôt/de fichiers. Cette commande est utile pour prévoir et éviter les conflits de merge.

Outils utiles quand Git ne parvient pas à démarrer un merge

git checkout

checkout permet d'annuler les changements apportés à des fichiers ou de changer des branches.

git reset --mixed

reset peut être utilisé pour annuler des changements apportés au répertoire de travail et à la zone de staging.

Outils utiles en cas de conflits Git durant un merge

git merge --abort

L'exécution de git merge avec l'option --abort quittera le processus de merge et réinitialisera la branche à son état antérieur au merge.

git reset

La commande git reset peut être utilisée durant un conflit de merge pour réinitialiser les fichiers en conflit à un état fonctionnel connu.

Résumé

Les conflits de merge peuvent être une expérience intimidante. Heureusement, Git offre des outils puissants pour aider à naviguer et à résoudre les conflits. À lui seul, Git peut gérer la plupart des merges grâce à ses fonctionnalités de merge automatique. Un conflit survient lorsque deux branches distinctes ont modifié la même ligne dans un fichier, ou lorsqu'un fichier a été supprimé dans une branche, mais modifié dans l'autre. Les conflits surviennent le plus souvent dans un environnement de travail en équipe.

Il existe de nombreux outils pour aider à résoudre les conflits de merge. Git dispose de nombreux outils de ligne de commande dont nous avons parlé ici. Pour en savoir plus sur ces outils, consultez les pages dédiées aux commandes git log, git reset, git status, git checkout et git reset. Outre Git, de nombreux outils tiers offrent des fonctionnalités simplifiées pour résoudre les conflits de merge.

Prêt à tester la création de branches ?

Essayez ce tutoriel interactif.

Démarrez maintenant