Utiliser des branches

git merge

 

Dans Git, le merge permet de reconstituer un historique forké. La commande git merge vous permet de sélectionner les lignes de développement indépendantes créées grâce à git branch et de les intégrer à une seule branche.

Remarque : toutes les commandes présentées ci-après font un merge dans la branche actuelle. La branche actuelle sera mise à jour pour faire état du merge, tandis que la branche cible restera inchangée. À nouveau, cela signifie que la commande git merge est souvent utilisée en association avec git checkout pour sélectionner la branche actuelle et git branch -d pour supprimer la branche cible obsolète.

Fonctionnement

git merge combinera plusieurs séquences de commits en un historique unifié. Dans les cas d'usage les plus fréquents, git merge est utilisée pour combiner deux branches. Les exemples suivants dans ce document se concentreront sur ce modèle de merge de branches. Dans ces scénarios, git merge prend deux pointeurs de commit, généralement les pointes de la branche, et recherche un commit de base commun aux deux. Dès que c'est chose faite, il crée un « commit de merge » qui combine les changements de chaque séquence de commit de merge dans la file d'attente.

Supposons que nous ayons une nouvelle fonctionnalité de branche basée sur la branche principale (master). Nous souhaitons maintenant merger cette branche de fonctionnalité dans master.

Appeler cette commande permettra de merger la fonctionnalité de branche spécifiée dans la branche actuelle, disons master. Git déterminera automatiquement l'algorithme de merge (abordé ci-dessous).

Les commits de merge sont uniques par rapport aux autres commits, car ils ont deux commits parent. En créant un commit de merge, Git essaiera de merger automatiquement les historiques distincts à votre place. Si Git rencontre des données différentes dans les deux historiques, il ne pourra pas les combiner automatiquement. Ce scénario constitue un conflit de contrôle de version, et l'intervention d'un utilisateur sera nécessaire pour continuer. 

Préparation du merge

Avant d'effectuer un merge, plusieurs étapes de préparation doivent être exécutées pour garantir que tout se déroule en douceur.

Confirmer la branche cible

Exécutez la commande git status pour vous assurer que HEAD pointe vers la branche recevant le merge correspondant. Si nécessaire, exécutez git checkout <receiving> pour passer à la branche cible. Dans notre cas, nous exécuterons git checkout master.

Fetch des derniers commits distants

Assurez-vous que la branche cible et la branche mergée sont mises à jour avec les derniers changements distants. Exécutez la commande git fetch pour faire un pull des derniers commits distants. Une fois que le fetch est terminé, assurez-vous que la branche principale (master) est à jour en exécutant la commande git pull.

Merge

Après avoir exécuté les étapes de « préparation au merge » déjà évoqués, un merge peut être initié en exécutant git merge <branch name> où <branch name> désigne le nom de la branche qui sera mergée dans la branche cible.

Fast-forward merge

Un fast-forward merge peut avoir lieu lorsque le chemin entre la pointe de la branche actuelle et la branche cible est linéaire. Plutôt que de réellement merger les branches, Git doit simplement déplacer (en « fast-forward ») la pointe de la branche actuelle vers celle de la branche cible pour intégrer les historiques. Cette opération est possible, puisque tous les commits accessibles à partir de la branche cible le deviennent également via la branche actuelle. Par exemple, un fast-forward merge de some-feature dans la branche principale (master) peut être schématisé comme suit :

Toutefois, un fast-forward merge n'est pas possible si les branches ont divergé. Lorsque le chemin vers la branche cible n'est pas linéaire, Git n'a d'autre choix que de combiner les branches grâce à un merge à trois branches. Les merges à trois branches utilisent un commit dédié pour lier deux historiques. La nomenclature vient du fait que Git utilise trois commits pour générer le commit de merge : les deux pointes de branche et leur ancêtre commun.


Bien que vous puissiez utiliser ces deux stratégies de merge, de nombreux développeurs aiment utiliser les fast-forward merges (simplifiés par le rebase) pour la correction de petites fonctionnalités ou de bugs mineurs, tout en réservant les merges à trois branches pour l'intégration de fonctionnalités utilisées à plus long terme. Le cas échéant, le commit de merge résultant permet d'établir un lien symbolique entre deux branches.

Notre premier exemple présente un fast-forward merge. Le code ci-dessous crée une nouvelle branche, y ajoute deux commits, puis l'intègre dans la ligne principale grâce à un fast-forward merge.

# Vous démarrez une nouvelle fonctionnalité
git checkout -b new-feature master
# Vous éditez certains fichiers
git add <file>
git commit -m "Start a feature"
# Vous éditez certains fichiers
git add <file>
git commit -m "Finish a feature"
# Vous mergez la branche « new-feature »
git checkout master
git merge new-feature
git branch -d new-feature

Il s'agit d'un workflow commun pour les branches topic éphémères qui sont davantage utilisées comme un développement isolé que comme un outil organisationnel pour les fonctionnalités à plus long terme.

Remarque : Git ne devrait pas réagir à la commande git branch -d, puisque la fonctionnalité new-feature est désormais accessible à partir de la branche principale.

Si vous avez besoin d'un commit de merge au cours d'un fast-forward merge à des fins d'archivage, vous pouvez exécuter la commande git merge avec l'option --no-ff.

git merge --no-ff <branch>

Cette commande merge la branche spécifiée dans la branche actuelle, mais génère toujours un commit de merge (même s'il s'agissait d'un fast-forward merge). C'est utile pour documenter tous les merges qui se produisent dans votre dépôt.

Merge 'à trois sources' (3-way merge)

L'exemple suivant est très similaire, à cela près qu'un merge à trois branches est nécessaire, car la branche principale (master) avance tandis que la fonctionnalité est en cours de développement. Ce scénario n'est pas rare lorsque les fonctionnalités sont volumineuses ou que plusieurs développeurs travaillent simultanément sur un même projet.

Vous débutez une nouvelle fonctionnalité
git checkout -b new-feature master
# Vous éditez certains fichiers
git add <file>
git commit -m "Start a feature"
# Vous éditez certains fichiers
git add <file>
git commit -m "Finish a feature"
# Vous développez la branche master
git checkout master
# Vous éditez certains fichiers
git add <file>
git commit -m "Make some super-stable changes to master"
# Vous faites un merge dans la nouvelle branche de fonctionnalité
git merge new-feature
git branch -d new-feature

Remarque : Git ne peut réaliser un fast-forward merge, puisqu'il est impossible de déplacer master vers new-feature sans backtracking.

Pour la plupart des workflows, new-feature serait une fonctionnalité bien plus volumineuse, dont le développement nécessiterait beaucoup de temps et qui justifierait l'apparition de nouveaux commits sur master entre-temps. Si votre branche de fonctionnalité est aussi petite que celle illustrée dans l'exemple ci-dessus, il est préférable d'en faire un rebase sur master et de réaliser un fast-forward merge. Ainsi, l'historique du projet ne sera pas pollué par des commits de merge superflus.

Résolution de conflits

Si les deux branches que vous essayez de merger modifient toutes les deux la même partie du même fichier, Git ne peut pas déterminer la version à utiliser. Lorsqu'une telle situation se produit, Git s'arrête avant le commit de merge, afin que vous puissiez résoudre manuellement les conflits.

L'avantage du merge dans Git est que le workflow habituel d'édition, de staging et de commit est utilisé pour résoudre les conflits de merge. Lorsque vous faites face à un conflit de merge, lancez la commande git status pour afficher les fichiers en conflit. Par exemple, si les deux branches modifient la même section de hello.py, le message suivant s'affiche à l'écran :

On branch master
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
both modified: hello.py

Présentation des conflits

Lorsque Git rencontre un conflit au cours d'un merge, il éditera le contenu des fichiers affectés avec des indicateurs visuels qui marquent les deux côtés du contenu en conflit. Ces marqueurs visuels sont les suivants : <<<<<<<, ======= et >>>>>>>. Il s'avère utile de rechercher ces indicateurs dans un projet au cours d'un merge pour savoir où des conflits doivent être résolus.

here is some content not affected by the conflict
<<<<<<< master
this is conflicted text from master
=======
this is conflicted text from feature branch
>>>>>>> feature branch;

En principe, le contenu situé avant le marqueur ======= représente la branche cible et la partie après celui-ci, la branche mergée.

Lorsque vous avez identifié les sections en conflit, vous pouvez faire le nécessaire pour résoudre le problème. Lorsque vous êtes prêt à terminer le merge, il vous suffit d'exécuter git add sur le ou les fichiers en conflit pour signaler à Git que le problème est résolu. Ensuite, lancez la commande git commit normalement pour générer le commit de merge. Le processus est exactement le même que pour commiter un instantané classique. Ainsi, les développeurs n'auront aucune difficulté à gérer leurs merges.

Notez que des conflits de merge se produiront également en cas de merge à trois branches. Il n'est pas possible d'avoir des changements en conflit dans un fast-forward merge. 

Résumé

Ce document présente la commande git merge. Le merge est un processus essentiel lorsque vous travaillez avec Git. Nous avons déjà parlé des mécanismes internes à la base d'un merge, et des différences entre un fast-forward merge et un véritable merge à trois branches. Voici quelques enseignements clés :
 

  1. Le merge Git combine plusieurs séquences de commits en un historique de commits unifié.
  2. Il existe deux types principaux de merges Git : fast-forward et à trois branches
  3. Git peut merger automatiquement les commits, sauf si des changements entrent en conflit dans les deux séquences de commit.

Ce document intégrait et répertoriait d'autres commandes Git, notamment : git branch, git pull, and git fetch. Consultez leurs pages respectives pour en savoir plus. 

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

Essayez ce tutoriel interactif.

Démarrer maintenant