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 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 :
Ressource connexe
Commande git log avancée
DÉCOUVRIR LA SOLUTION
Découvrir Git avec Bitbucket Cloud
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"
[main (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éez un répertoire nommé
git-merge-test,
, basculez vers ce répertoire, puis initialisez-le en tant que nouveau dépôt Git. - Créez un fichier texte
merge.txt
avec du contenu. - Ajoutez
merge.txt
au dépôt et commitez-le.
Nous disposons à présent d'un nouveau dépôt avec une branche (main
) 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 main
Switched to branch 'main'
echo "content to append" >> merge.txt
git commit -am"appended content to merge.txt"
[main 24fbe3c] appended content to merge.tx
1 file changed, 1 insertion(+)
Cette chaîne de commandes fait un check-out de la branche (main
), 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 (main
) 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 main
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
permet de 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 modifier des branches.
git reset --mixed
reset
peut être utilisée 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
mettra fin au 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.
Partager cet article
Thème suivant
Lectures recommandées
Ajoutez ces ressources à vos favoris pour en savoir plus sur les types d'équipes DevOps, ou pour les mises à jour continues de DevOps chez Atlassian.