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 avec 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 courante. La branche courante 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 courante et git branch -dsupprimer 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 merging de branches. Dans ces scénarios, git merge prend deux pointeurs de commit, généralement les pointes de la branche, et recherchera un commit de base commun aux deux. Dès que Git trouve un commit de base commun, il créera un nouveau « 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 master. Nous souhaitons maintenant faire un merge de cette branche de fonctionnalité dans la branche master.

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

Les commits de merge sont uniques par rapport aux autres commits en ce qu'ils ont deux commits parents. En créant un commit de merge, Git essaiera de faire un merge automatique des 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 Git aura besoin de l'intervention d'un utilisateur pour continuer. 

Préparation du merge

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

Confirmer la branche cible

Exécutez la commande git status pour vous assurer que HEAD pointe vers la branche recevant le merge correspondante. Si nécessaire, exécutez git checkout <branche cible> 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 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 <nom de branche> où <nom de branche> 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 courante et la branche cible est linéaire. Plutôt que de réellement faire un merge des branches, tout ce que Git doit faire pour intégrer les historiques consiste à déplacer (en « fast-forward ») la pointe de la branche courante vers celle de la branche cible. Il est ainsi possible de combiner les historiques, puisque tous les commits accessibles à partir de la branche cible le deviennent également via la branche courante. Par exemple, un fast-forward merge de some-feature dans la branche 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 avec 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 avec un fast-forward merge.

# Vous démarrez une nouvelle fonctionnalité
git checkout -b new-feature master

# Vous éditez certains fichiers
git add <fichier>
git commit -m "Start a feature"

# Vous éditez certains fichiers
git add <fichier>
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 utilisées à 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 master.

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 <branche>

Cette commande fait un merge de la branche spécifiée dans la branche courante, mais génère toujours un commit de merge (même s'il s'agissait d'un fast-forward merge). Cela 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 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 <fichier>
git commit -m "Start a feature"
# Vous éditez certains fichiers
git add <fichier>
git commit -m "Finish a feature"
# Vous développez la branche master
git checkout master
# Vous éditez certains fichiers
git add <fichier>
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 de la rebaser 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, d'indexation 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 :

Sur la branche master
Chemins non mergés :
(utilisez « git add/rm ... » pour indiquer la résolution)
tous modifiés : 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.

Voici du contenu qui n'est pas affecté par le conflit
<<<<<<< master
Voici du texte en conflit de la branche master
=======
Voici du texte en conflit de la branche de fonctionnalité
>>>>>>> branche de fonctionnalité ;

En principe, le contenu situé avant le marqueur ======= représente la branche cible et la partie après ce marqueur représente 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(s) fichier(s) 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. 

Summary

Ce document présente la commande git merge. Le merging 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. Git merging combines sequences of commits into one unified history of commits.
  2. There are two main ways Git will merge: Fast Forward and Three way
  3. Git can automatically merge commits unless there are changes that conflict in both commit sequences.

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

Prêt à tester le branching ?

Essayez ce tutoriel interactif.

Démarrez maintenant