Débuter avec Git partie 5 : fusionner des branches

Pour continuer dans cette série sur comment débuter avec Git, nous avions vu dans le précédent article précisément ce que sont les branches Git. Nous allons aujourd’hui présenter pourquoi fusionner des branches et comment s’y prendre.

Mise en place

Pour cet article nous créons un dépôt distant sur notre Gitlab (comme expliqué dans la partie 1), puis nous le clonons sur notre machine locale.

$ git clone https://gitlab.com/chaica/merge-branches.git
Cloning into 'merge-branches'…
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
$ cd merge-branches/
$ ls
README.md

Notre dépôt initial contient simplement un fichier README.md, créé par Gitlab, contenant un titre en langage Markdown

$ cat README.md
# merge-branch

Nous allons maintenant ajouter un sous-titre à ce fichier pour un prochain exemple.

$ echo -e "\n## sous-titre" >> README.md 
$ git commit README.md -m "add first subtitle"
[master 2059805] add first subtitle
 1 file changed, 2 insertions(+)

À ce niveau, nous avons donc deux commits qui constituent notre branche master, comme nous l’indique la commande git log.

$ git log
commit 20598053fb2c3e55f95e2521dfa804739abd7d8a
Author: Carl Chenet chaica@ohmytux.com
Date:   Fri Jun 21 10:22:00 2019 +0200

 add first subtitle

commit 11cb68a24bed5236972138a1211d189adb4512a8 (origin/master, origin/HEAD)
Author: Carl Chenet chaica@ohmytux.com
Date:   Fri Jun 21 08:18:56 2019 +0000

 Initial commit

Mise en place un peu longue, mais qui nous a permis de réviser quelques commandes fondamentales. Nous entrons dans le vif du sujet.

Création d’une nouvelle branche

Un client nous demande une évolution du code. Afin de ne pas toucher à la branche master qui contient le code de référence courant, nous allons compliquer maintenant un peu les choses en créant une nouvelle branche nommée branch-with-foo. Cette étape a déjà été expliquée dans la partie 4 plus en détail.

$ git checkout -b branch-with-foo
Switched to a new branch 'branch-with-foo'

Nous créons immédiatement un fichier nommé foo dans cette branche que nous ajoutons et enregistrons dans la foulée.

$ echo "this is a foo file" > foo
$ git add foo && git commit foo -m "add foo file"
[branch-with-foo d9afaa2] add foo file
 1 file changed, 1 insertion(+)
 create mode 100644 foo

Divergence des branches

Nous revenons maintenant sur master et nous créons un fichier bar que nous enregistrons aussi dans la foulée.

$ git checkout master
$ echo "this is a bar file" > bar
$ git add bar && git commit bar -m "add bar file"
[master 222c618] add bar file
 1 file changed, 1 insertion(+)
 create mode 100644 bar

Faisons une pause, nous venons de créer notre première divergence entre nos branches, nous avons créé un embranchement dans l’historique de nos commits, les deux derniers commits n’appartenant plus aux mêmes branches.

Débuter avec git : divergence de branches

Le schéma présente la divergence entre la branche master et la branche branch-with-foo. La première contient un fichier bar, la seconde un fichier foo. Bien, il est temps de passer aux choses sérieuses.

Fuuuuuuuuuusion

Le besoin que nous avions de créer une nouvelle branche a disparu, le client a annulé le projet.

Bon, nous allons réintégrer les modifications de cette branche dans la branche master. Nous nous positionnons dans la branche master, ou d’une manière générale la branche à laquelle nous souhaitons réintégrer les modifications d’une autre, et nous passons la commande suivante :

$ git checkout master
$ git merge branch-with-foo -m "Merge branch 'branch-with-foo'"
Merge made by the 'recursive' strategy.
  foo | 1 +
  1 file changed, 1 insertion(+)
  create mode 100644 foo

La sortie de la commande nous précise ce qui s’est passé : un fichier foo (celui de la branche branch-with-foo) a été créé dans la branche courante master.

Débuter avec git : fuuuuuuuuusion

Jetons un oeil à l’historique avec la commande git log avec l’option –graph qui va nous présenter une représentation graphique de notre historique et l’option –oneline afin de rendre la commande moins verbeuse.

$ git log --graph --oneline
* 69fa060 (HEAD -> master) Merge branch 'branch-with-foo'
|\  
| * d9afaa2 (branch-with-foo) add foo file
* |222c618 add bar file
|/  
* 2059805 add first subtitle
* 11cb68a (origin/master, origin/HEAD) Initial commit 

Cette représentation est très parlante. Au niveau de l’histoire elle va de haut en bas, le haut étant le commit le plus récent et le plus bas le plus vieux. Une étoile (*) est un commit, avec son message à droite , et le nom de la branche s’il y ambiguité.

Nous reprenons notre dessin précédent et le faisons évoluer.

Débuter avec git :  fusion de branches

Nous avons bien fusionné la branche branch-with-foo dans master. Fusion réussie.

Sauver son code et ses branches

Avant de s’arrêter aujourd’hui, n’oublions pas de pousser tout ce que nous avons fait vers notre dépôt Gitlab distant. Nous commençons par la branche master.

$ git push 
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 8 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (11/11), 975 bytes | 975.00 KiB/s, done.
Total 11 (delta 2), reused 0 (delta 0)
To https://gitlab.com/chaica/merge-branches.git
   11cb68a..69fa060  master -> master

La dernière ligne indique bien que nous avons poussé depuis notre branche master locale vers notre branche master distante présent sur notre Gitlab.

Passons à la branche branch-with-foo, qui, même si elle a été fusionnée dans master, existe toujours. Pourquoi ? Car le nom de la branche branch-with-foo est un pointeur, un indicateur qui désigne le dernier commit connu de cette branche. Rien de plus.

Pour changer un peu nous varions la syntaxe de notre commande git push en utilisant l’option –all afin de pousser toutes les branches locales vers le dépot distant. Nous n’en avons qu’une ici, branch-with-foo.

$ git push --all
Total 0 (delta 0), reused 0 (delta 0)
remote: 
remote: To create a merge request for branch-with-foo, visit:
remote:   https://gitlab.com/chaica/merge-branches/merge_requests/new?merge_request%5Bsource_branch%5D=branch-with-foo
remote: 
To https://gitlab.com/chaica/merge-branches.git
[new branch]      branch-with-foo -> branch-with-foo 

Point très intéressant, nous voyons que les lignes commençant par remote: proviennent du Gitlab, qui nous indiquent comment créer une demande de fusion (Merge Request). Inutile, nous avons déjà fusionné. Mais ce sera intéressant dans le cadre du travail collaboratif. Ce sera pour un prochain article.

Une demande de fusion sur Gitlab

La dernière ligne nous confirme que nous avons bien poussé la branche locale branch-with-foo vers la branche du dépôt Gitlab distant nommée également branch-with-foo.

Conclusion

Nous avons vu aujourd’hui la fusion de branches Git. Cette opération permet de récupérer le travail réalisé dans une autre branche, en divergence du code “principal” que nous conservons – comme bonne pratique dans l’industrie en général – dans la branche master. C’est le cas le plus courant.

Vous devriez quasi systématiquement commencer par créer une nouvelle branche quand vous envisagez d’introduire du nouveau code dans un dépôt Git, afin de ne pas travailler directement vous-même dans master. Pourquoi pas ? C’est ce que nous verrons dans le prochain article de cette série.

Me suivre sur les réseaux sociaux

N’hésitez pas à me suivre directement sur les différents sociaux pour suivre au jour le jour mes différentes projets dans le Logiciel Libre :

Suivre l’actualité du Logiciel Libre et Open Source francophone

Abonnez-vous au Courrier du hacker, une newsletter hebdomadaire résumant le meilleur de l’actualité francophone du Logiciel Libre et Open Source. Déjà plus de 90 numéros et 2000 abonnés.

Le Courrier du hacker

5 thoughts on “Débuter avec Git partie 5 : fusionner des branches

  1. Bonjour Carl,

    Cette série d’articles sur Git est très intéressante et bienvenue car Git est L’OUTIL A UTILISER pour travailler sur un projet de nos jours. Je dois dire que c’est un formidable outil que Linus à créer …en 15 jours.

    La dernière partie m’a “sauvé la vie” alors que je venais juste de demander à un collègue de m’aider à ce niveau là car je ne voyais qu’une des 4 branches annexes sur Github que j’avais crée.

    Il y a quand même une grande question que je n’ai pas encore vu et, qui à mon avis pas mal de monde se pose.

    Elle concerne la collaboration des projets sur Github ou Gitlab. On fork un projet, on commence par l’étudier, si on a le temps on commence à travailler sur une fonctionnalité. Mais entre-temps, le projet initial a évolué et une nouvelle version est apparue.
    Comment mettre proprement notre version sur Github ou Gitlab puis notre propre version sur notre ordinateur ?

    Par avance, merci. Et encore des articles sur Git…..

  2. Je viens cette fois-ci non pas avec une question générale mais avec un cas pratique qui vient juste de m’arriver.

    J’ai crée sur un de mes projets une branche que j’ai appelé internationnal-translation (en fait peu importe le nom). J’ai travaillé dessus toute la journée. Mais par moment, je suis retourné sur master pour faire directement des modifications afin que les “utilisateurs” (s’il y en a!!) puissent directement avoir. Puis je suis retourné sur ma branche. J’ai fais encore des modifications. Je suis aussi retourné sur master faire d’autres modifications dont la suppression et deplacement de dossiers/fichiers. Puis je suis retourné sur cette branche. Je n’ai pas pusher avant sur master ni sur la branche. Quand j’ai fini j’ai envoyer mon travail sur Github. verification de ma branche internationnal-translation, tout mon travail y est. verification de la branche master, rien n’y est.Je veux passer sur master : impossible.
    Que se passe-t-il ? Pourquoi ? Aurait-je du pusher quand il me l’a dis ? Comment resoudre ce problème.
    Il me dit de valider ou de remiser. J’ai bien tenter de rebaser, de merger mais sans grand succès.
    Voici le code :
    git checkout master
    error: Vos modifications locales aux fichiers suivants seraient écrasées par l'extraction :
    .idea/workspace.xml
    Veuillez valider ou remiser vos modifications avant de basculer de branche.
    Abandon
    [olivier@thorielle StreamCap]$ git rebase internationnal-translation master
    error: Impossible de rebase : vous avez des modifications non indexées.
    error: Veuillez les valider ou les remiser.
    [olivier@thorielle StreamCap]$ git merge internationnal-translation
    Déjà à jour.

    • Des modifications sur .idea/workspace.xml n’ont pas été committées dans ta branche. Donc tu risques de les perdre. Il faut les commiter ou revenir à l’état initial de ce fichier pour continuer.

      • J’ai résolu cette issue en ajoutant ce fichier avec la commande git add .
        J’ai pu ainsi basculer sur la branche master sans problème.
        Mais j’en ai eu plein d’autres ^_^

        Ce fichier est assez ennuyant puisqu’il m’a posé problème par la suite. j’ai donc décider de ne pas le prendre en compte dans .gitignore. J’ai pu ainsi passer dans master pour intégrer ma branche internationnal-translation. Cependant je n’ai pas voulu supprimer cette branche. j’ai voulu ensuite la maj par rapport à master et là rebelote. ce fichier m’a encore embêter alors que je l’avais tagué et rien fait hormis changer de branche.
        Et de ce fait la maj de cette branche a échoué.
        Voici le code:
        [olivier@thorielle streamcap]$ git rebase origin/master
        Rembobinage préalable de head pour pouvoir rejouer votre travail par-dessus...
        Application de modification gitignore file for removing workspace.xml file
        Utilisation de l'information de l'index pour reconstruire un arbre de base...
        M .idea/workspace.xml
        .git/rebase-apply/patch:11: trailing whitespace.
        **/.idea/workspace.xml
        warning: 1 ligne a ajouté des erreurs d'espace.
        Retour à un patch de la base et fusion à 3 points...
        Fusion automatique de .idea/workspace.xml
        CONFLIT (contenu) : Conflit de fusion dans .idea/workspace.xml
        error: Échec d'intégration des modifications.
        le patch a échoué à 0001 modification gitignore file for removing workspace.xml file
        astuce: Utilisez 'git am --show-current-patch' pour visualiser le patch en échec
        Resolve all conflicts manually, mark them as resolved with
        "git add/rm ", then run "git rebase --continue".
        You can instead skip this commit: run "git rebase --skip".
        To abort and get back to the state before "git rebase", run "git rebase --abort".
        [olivier@thorielle streamcap]$ git am --show-current-patch
        commit 9e33ffbb679c5350b7df19291055e05a29b4a140 (origin/internationnal-translation, internationnal-translation)
        Author: olielvewen
        Date: Sun Oct 13 17:18:10 2019 +0200

        modification gitignore file for removing workspace.xml file
        [olivier@thorielle streamcap]$ git status
        rebasage en cours ; sur 724d210
        Vous êtes en train de rebaser la branche 'internationnal-translation' sur '724d210'.
        (réglez les conflits puis lancez "git rebase --continue")
        (utilisez "git rebase --skip" pour sauter ce patch)
        (utilisez "git rebase --abort" pour extraire la branche d'origine)

        Modifications qui seront validées :
        (utilisez "git restore --staged ..." pour désindexer)
        modifié : ../.gitignore

        Chemins non fusionnés :
        (utilisez "git restore --staged ..." pour désindexer)
        (utilisez "git add ..." pour marquer comme résolu)
        modifié des deux côtés : ../.idea/workspace.xml

        Modifications qui ne seront pas validées :
        (utilisez "git add ..." pour mettre à jour ce qui sera validé)
        (utilisez "git restore ..." pour annuler les modifications dans le répertoire de travail)
        modifié : ../.gitignore
        [olivier@thorielle streamcap]$ git rebase --abort
        [olivier@thorielle streamcap]$ git status
        Sur la branche internationnal-translation
        Votre branche est à jour avec 'origin/internationnal-translation'.

        rien à valider, la copie de travail est propre
        [olivier@thorielle streamcap]$ git checkout master
        Basculement sur la branche 'master'
        Votre branche est à jour avec 'origin/master'.
        [olivier@thorielle streamcap]$ git branch
        brazilian-translation
        french-translation
        german-translation
        internationnal-translation
        * master

        Comme tu as pu le voir, je m’en suis sorti en annulant les modifications sur ce fichier incompréhensible au moins pour moi.

        Comment ne plus être embêté par ce fichier sachant que normalement je l’ai tagué dans gitignore de la manière suivante:
        **/.idea/workspace.xml

        La solution la plus simple (et donc la plus sale) pour mettre ma branche internationnal-translation serait de la supprimer puis de la recréer à partir de master mais je pense qu’il est plus propre et réalisable en se servant de la commande git rebase origin/master (en étant dans cette branche) Est-ce la bonne méthode (sachant que la branche master pointe vers le dernier commit n 724d210 ? Preuve ici : https://github.com/olielvewen/StreamCap/commits/master
        J’aimerai être sur à moins qu’il faille utiliser merge ?
        Aurai-je du sauter ce patch ou l’extraire.
        Quelle est la methode la plus propre que toi tu ferais (meme si tu n’aurais pas eu ce genre de problème).

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *