Ce post est la suite de notre article GIT - 1 - la base.

9. Branche

Comme nous l’avons évoqué précédemment, la notion la plus importante de Git est la notion de branche.

Suite à notre dernière commande (git checkout 38db667), le dépôt n’est plus rattaché à une branche.

Mais nous pouvons en créer une!

C’est ce que nous allons faire avec la commande suivante :

git checkout -b onFirstCommit

A présent nous sommes dans la branche onFirstCommit.

Git a créé une nouvelle branche dans le dépôt à partir du premier commit présent dans l’historique : 38db667.

Contrairement à d’autres gestionnaires de source (TFS par exemple), Git ne crée pas un dossier par branche.

Lorsque l’on change de branche, le contenu du dossier est restauré à l’état où il était après le commit courant, ici 38db667.

Revenez dans la branche main :

git checkout main

Voici ce que vous devriez avoir à présent :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg
* d17c78e - (HEAD -> main) ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

10. Head

Dans votre dépôt, vous avez une branche onFirstCommit et une branche main.

La mention HEAD (pour tête de lecture cf. cassettes à bandes magnétiques 👴) vous informe sur quelle branche est votre dépôt actuellement :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg
* d17c78e - (HEAD -> main) ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Vous êtes dans la branche main et il existe une autre branche onFirstCommit.

11. Rebase, Merge, Cherry-Pick

Dans cette partie nous allons apprendre 3 techniques indispensables pour se sortir de presque toutes les situations :

  1. Rebase,
  2. Merge,
  3. et Cherry-Pick.

11.1 Rebase

Cette commande a pour objectif d’appliquer tous les commits d’une branche à une autre depuis le dernier commit commun aux deux branches.

Créez de nouveau une branche :

git checkout -b branchToRebase

Ajoutez un fichier addedOnBranchToRebase:

touch addedOnBranchToRebase.txt

Effectuez un commit contenant cet ajout de fichier :

 git add .
 git commit -m "ajout de addedOnBranchToMerge.txt"

Ensuite ajoutez du texte dans ce fichier et faites de nouveau un commit avec cette modification :

 git add .
 git commit -m "modification de addedOnBranchToRebase.txt"

Voici le résultat :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (branchToRebase)
$ git lg
* d2f1efe - (HEAD -> branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - (main) ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Repartez dans la branch main :

git checkout main

Modifiez le fichier 2emeFichier.txt et effectuez un commit dans main :

 git add .
 git commit -m "modification de 2emeFichier.txt"

Utilisez la commande suivante pour voir toutes les branches actuelles :

git lg --branches=*

Vous devez avoir ceci :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg --branches=*
* fd34ef1 - (HEAD -> main) modification de 2emeFichier.txt
| * d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
| * d609c65 - ajout de addedOnBranchToMerge.txt (Alex 2 minutes ago)
|/
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Nous allons à présent effectuer un rebase de la branche branchToRebase dans main :

git rebase branchToRebase

Utilisez de nouveau la commande :

git lg --branches=*

Voici le résultat :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg --branches=*
* 9ef588c - (HEAD -> main) modification de 2emeFichier.txt
* d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Git a intégré les commits de branchToRebase dans main en partant du dernier commit commun entre les deux branches, puis il a ajouté à la suite (toujours dans main) les commits qui n’étaient pas dans la branche branchToRebase.

Voici ce que nous avions avant le rebase :

                              fd34ef1                       (main)
                            /
    ...----38db667---d17c78e---d609c65---d2f1efe            (branchToRebase)

Voici ce que nous avons après :

                                                  fd34ef1   (main)
                                                /
    ...----38db667---d17c78e---d609c65---d2f1efe            (branchToRebase)

main contient à présent les commits de branchToRebase plus le commit fd34ef1 qui n’est pas présent dans branchToRebase

11.2 Merge

Merge signifie fusionner.

Comme son nom l’indique cette commande a pour objectif de fusionner l’historique de deux branches en un nouveau commit de fusion.

Créez de nouveau une branche :

git checkout -b branchToMerge

Ajoutez un fichier addedOnBranchToMerge:

touch addedOnBranchToMerge.txt

Effectuez un commit contenant cet ajout de fichier :

 git add .
 git commit -m "ajout de addedOnBranchToMerge.txt"

Ensuite ajoutez du texte dans ce fichier et faites de nouveau un commit avec cette modification :

 git add .
 git commit -m "modification de addedOnBranchToMerge.txt"

Voici le résultat :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (branchToMerge)
$ git lg
* 97115b5 - (HEAD -> branchToMerge) modification de addedOnBranchToMerge.txt
* c55aecb - ajout de addedOnBranchToMerge.txt
* 9ef588c - (main) modification de 2emeFichier.txt
* d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Repartez dans la branch main.

git checkout main

Modifiez le fichier 2emeFichier.txt et effectuez un commit dans main

 git add .
 git commit -m "2ème modification de 2emeFichier.txt"

Utilisez la commande suivante pour voir toutes les branches actuelles :

git lg --branches=*

Voici l’état des branches :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg --branches=*
* 2805653 - (HEAD -> main) 2ème modification de 2emeFichier.txt
| * 97115b5 - (branchToMerge) modification de addedOnBranchToMerge.txt
| * c55aecb - ajout de addedOnBranchToMerge.txt
|/
* 9ef588c - modification de 2emeFichier.txt
* d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Nous allons effectuer un merge de la branche branchToMerge dans main :

git merge branchToMerge

Voici l’état des branches après merge:

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg --branches=*
*   cc665a6 - (HEAD -> main) Merge branch 'branchToMerge'
|\
| * 97115b5 - (branchToMerge) modification de addedOnBranchToMerge.txt
| * c55aecb - ajout de addedOnBranchToMerge.txt
* | 2805653 - 2ème modification de 2emeFichier.txt
|/
* 9ef588c - modification de 2emeFichier.txt
* d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Git a intégré les modifications de branchToMerge dans main créant un nouveau commit de fusion contenant toutes les modifications des commits qui n’étaient pas dans la branche main.

Voici ce que nous avions avant le merge :

                        c55aecb---97115b5         (branchToMerge)
                      /
    ...---d2f1efe---9ef588c---2805653             (main)

Voici ce que nous avons après :

                         c55aecb---97115b5        (branchToMerge)
                        /                 \
    ...---d2f1efe---9ef588c---2805653---cc665a6   (main)

11.3 Cherry-Pick

Cherry-pick signifie cueillir les cerises.

Cette commande permet de récupérer n’importe quel commit dans la branche courante.

Elle vous permettra de vous sortir de pas mal de situations compliquées.

Pour tester cette commande, ajoutez un fichier nommé cherryToPick.txt dans la branche main puis effectuez un commit :

 touch cherryToPick.txt
 git add .
 git commit -m "ajout de cherryToPick.txt"

Vous devez obtenir cela à présent :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (main)
$ git lg
* cbc6bfa - (HEAD -> main) ajout de cherryToPick.txt
*   cc665a6 - Merge branch 'branchToMerge'
|\
| * 97115b5 - (branchToMerge) modification de addedOnBranchToMerge.txt
| * c55aecb - ajout de addedOnBranchToMerge.txt
* | 2805653 - 2ème modification de 2emeFichier.txt
|/
* 9ef588c - modification de 2emeFichier.txt
* d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - (onFirstCommit) creation du dépôt ajout de 1erFichier.txt

Ensuite allez sur la branche onFirstCommit :

 git checkout onFirstCommit

Et tapez la commande :

 git cherry-pick cbc6bfa

Vous avez récupéré le commit cbc6bfa dans votre branche courante et les modifications associées :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (onFirstCommit)
$ git lg
* 40bcc78 - (HEAD -> onFirstCommit) ajout de cherryToPick.txt (Alex 2 minutes ago)
* 38db667 - creation du dépôt ajout de 1erFichier.txt (Alex 49 minutes ago)

Mais son numéro de commit est différent : 40bcc78!

Voici ce que nous avions avant le Cherry Pick :

            d17c78e---...---cc665a6---cbc6bfa             (main)
          /
    38db667                                     (onFirstCommit)

Voici ce que nous avons après :

            d17c78e---...---cc665a6---cbc6bfa             (main)
          /
    38db667---40bcc78                           (onFirstCommit)

12 Les conflits

Nous avons vu comment effectuer des Rebases, des Merges et des Cherrys Picks.

Les cas présentés précédemment sont simples.

En pratique, lorsqu’on travaille sur plusieurs branches, il est fréquent de tomber sur des conflits.

Un conflit survient lorsque par exemple deux développeurs modifient le même contenu dans deux branches distinctes.

Nous allons en créer un volontairement!

Pour cela modifiez le fichier 1erFichier.txt dans la branche actuelle (onFirstCommit) et effectuez ensuite un commit sur cette branche :

 git add .
 git commit -m "modification de 1erFichier.txt sur onFirstCommit"

Voici le résultat :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (onFirstCommit)
$ git lg --branches=*
* 55452a6 - (HEAD -> onFirstCommit) modification de 1erFichier.txt sur onFirstCommit
* 40bcc78 - ajout de cherryToPick.txt
| * cbc6bfa - (main) ajout de cherryToPick.txt
| *   cc665a6 - Merge branch 'branchToMerge'
| |\
| | * 97115b5 - (branchToMerge) modification de addedOnBranchToMerge.txt
| | * c55aecb - ajout de addedOnBranchToMerge.txt
| * | 2805653 - 2ème modification de 2emeFichier.txt
| |/
| * 9ef588c - modification de 2emeFichier.txt
| * d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
| * d609c65 - ajout de addedOnBranchToMerge.txt
| * d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
|/
* 38db667 - creation du dépôt ajout de 1erFichier.txt

Dans notre exemple les commits d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt et 55452a6 - modification de 1erFichier.txt sur onFirstCommit vont entrer en conflit car nous avons modifié le fichier 1erFichier.txt au moment endroit (la première ligne)!

Effectuez un rebase de la branche main pour provoquer le conflit :

git rebase main

Ensuite allez dans le fichier 1erFichier.txt :

<<<<<< HEAD (Current Change)
première modification
=======
ceci va créer un conflit
>>>>>> 55452a6 (modification de 1erFichier.txt sur onFirstCommit) (Incoming Change)

Le Current Change est une modification contenue dans un commit provenant de la branche que nous sommes en train de “rebaser” dans notre branche courante.

Effectuez un git status

MINGW64 ~/source/repos/agora/github/mon-premier-repo (onFirstCommit|REBASE 1/1)
$ git status
interactive rebase in progress; onto cbc6bfa
Last command done (1 command done):
   pick 55452a6 modification de 1erFichier.txt sur onFirstCommit
No commands remaining.
You are currently rebasing branch 'onFirstCommit' on 'cbc6bfa'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
        both modified:   1erFichier.txt

no changes added to commit (use "git add" and/or "git commit -a")

Nous avons notre premier conflit à résoudre!

Il est possible de le résoudre manuellement.

Pour cela :

  • supprimez ‘««« HEAD’, ‘=======’ et ‘»»» 55452a6’.
  • gardez le texte que vous souhaitez conserver.
  • sauvegardez le fichier

Et utilisez ensuite les commandes suivantes :

git add .
git rebase --continue

Voici le résultat suite à la résolution du conflit :

MINGW64 ~/source/repos/agora/github/mon-premier-repo (onFirstCommit)
$ git lg
* 160c615 - (HEAD -> onFirstCommit) modification de 1erFichier.txt sur onFirstCommit 
* cbc6bfa - (main) ajout de cherryToPick.txt
*   cc665a6 - Merge branch 'branchToMerge'
|\
| * 97115b5 - (branchToMerge) modification de addedOnBranchToMerge.txt
| * c55aecb - ajout de addedOnBranchToMerge.txt
* | 2805653 - 2ème modification de 2emeFichier.txt
|/
* 9ef588c - modification de 2emeFichier.txt
* d2f1efe - (branchToRebase) modification de addedOnBranchToRebase.txt
* d609c65 - ajout de addedOnBranchToMerge.txt
* d17c78e - ajout de 2emeFichier.txt et modification de 1erFichier.txt
* 38db667 - creation du dépôt ajout de 1erFichier.txt

A présent votre branche contient tous les commits de la branche main et le commit qui posait problème 55452a6 modification de 1erFichier.txt sur onFirstCommit a été modifié en 160c615 modification de 1erFichier.txt sur onFirstCommit pour prendre en compte votre résolution de conflit!

La suite

Bon tout ça c’est très bien mais dans l’introduction de cette série d’article nous avions parlé de travailler en équipe et pour l’instant on travaille seul et en local… Nous allons donc voir comment échanger du code avec d’autres développeurs dans notre prochain post sur Git : 3 - les dépôts Git!