GitVoici le vingt-huitième volet de la formation Git. Dans mon précédent article, nous avons vu comment nous pouvons récupérer la travail effectué par un collègue sur sa propre branche. Cette manipulation nous permet de faire quelque chose d’inédit : modifier le travail d’un collaborateur sur sa propre branche. Voyons en détail les implications pratiques de cette façon de travailler.

Les cuistots et la sauce

Vous connaissez sans doute le fameux proverbe qui dit que trop de cuistots gâtent la sauce. La raison d’être de Git consiste justement à éviter ce genre de désordre. Continuons donc notre démonstration pratique des précédents articles :

$ cd formation-git/atelier-37/
$ ls -l
total 12
drwxrwxr-x. 3 kikinovak kikinovak 4096 Apr  7 09:38 clone-de-charles
drwxrwxr-x. 3 kikinovak kikinovak 4096 Apr  7 09:53 clone-de-paul
drwxrwxr-x. 3 kikinovak kikinovak 4096 Apr  7 08:10 clone-de-stephane
$ cd clone-de-charles/

Stéphane a demandé un coup de main à Charles. Il est en mal d’inspiration pour son poème Renouveau et aimerait bien que Charles lui fasse une suggestion sous forme d’un deuxième quatrain. Charles va donc jeter un œil :

$ git switch renouveau 
Switched to branch 'renouveau'
Your branch is up to date with 'origin/renouveau'.
$ cat Renouveau.md 
# Renouveau

*Stéphane Mallarmé*

Le printemps maladif a chassé tristement
L’hiver, saison de l’art serein, l’hiver lucide,
Et, dans mon être à qui le sang morne préside
L’impuissance s’étire en un long bâillement.

Charles va éditer le fichier Renouveau.md et ajouter un deuxième quatrain :

Des crépuscules blancs tiédissent sous mon crâne
Qu’un cercle de fer serre ainsi qu’un vieux tombeau
Et triste, j’erre après un rêve vague et beau,
Par les champs où la sève immense se pavane.

Il ajoute le fichier à l’index et effectue un commit :

$ git add Renouveau.md 
$ git commit -m "Renouveau: 2ème strophe."

Où en sommes-nous maintenant ? Adoptez le réflexe d’utiliser la commande git status lorsque vous vous posez cette question :

$ git status
On branch renouveau
Your branch is ahead of 'origin/renouveau' by 1 commit.
  (use "git push" to publish your local commits)

En langage tam-tam, cela signifie que la branche locale renouveau est « en avance » d’un commit par rapport à la branche de suivi à distance origin/renouveau. Là comme ailleurs, les suggestions de Git s’avèrent pertinentes, et il suffit d’un git push bien senti pour être à jour :

$ git push

AstuceNotez ici que Git « sait » que la branche locale renouveau dispose d’une branche de suivi distant correspondante origin/renouveau.

À présent, Stéphane peut songer à récupérer le travail de Charles. Voyons à quoi cela ressemble concrètement.

$ cd ../clone-de-stephane/
$ git status
On branch renouveau
Your branch is up to date with 'origin/renouveau'.

ImportantUn détail important : Git ne nous dit pas s’il y a du nouveau dans le dépôt public. C’est là un choix de conception du logiciel. Et très concrètement, c’est aux collaborateurs d’un projet d’aller vérifier de leur côté s’il y a du nouveau :

$ git fetch
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 9 (delta 1), reused 8 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), 1.37 KiB | 1.38 MiB/s, done.
From github.com:kikinovak/poesie-symboliste
   45cb7ce..69736f3  renouveau  -> origin/renouveau
 * [new branch]      aurore     -> origin/aurore
 * [new branch]      voyage     -> origin/voyage

AstucePeu importe la branche sur laquelle vous vous trouvez, git fetch va récupérer les nouveautés de toutes les branches de suivi à distance.

Stéphane se trouve bien sur la branche renouveau :

$ git branch
  main
* renouveau

Comment se fait-il alors que pour l’instant, il ne voie pas les modifications apportées par Charles ?

$ cat Renouveau.md 
# Renouveau

*Stéphane Mallarmé*

Le printemps maladif a chassé tristement
L’hiver, saison de l’art serein, l’hiver lucide,
Et, dans mon être à qui le sang morne préside
L’impuissance s’étire en un long bâillement.

La réponse à cela est simple et nous sert de base pour comprendre le workflow spécifique à Git :

$ git status
On branch renouveau
Your branch is behind 'origin/renouveau' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Pour une fois, nous allons faire fi de la suggestion de Git qui consisterait à invoquer git pull. Regardons plutôt ce qu’il nous dit une ligne plus haut. Your branch is behind 'origin/renouveau' by 1 commit, and can be fast-forwarded. En d’autres termes, nous pouvons effectuer un fast-forward merge avec la branche origin/renouveau :

$ git merge origin/renouveau 
Updating 45cb7ce..69736f3
Fast-forward
 Renouveau.md | 5 +++++
 1 file changed, 5 insertions(+)

Cette fois-ci, les modifications apportées par Charles ont bien été récupérées dans le clone de Stéphane :

$ cat Renouveau.md 
# Renouveau

*Stéphane Mallarmé*

Le printemps maladif a chassé tristement
L’hiver, saison de l’art serein, l’hiver lucide,
Et, dans mon être à qui le sang morne préside
L’impuissance s’étire en un long bâillement.

Des crépuscules blancs tiédissent sous mon crâne
Qu’un cercle de fer serre ainsi qu’un vieux tombeau
Et triste, j’erre après un rêve vague et beau,
Par les champs où la sève immense se pavane.

AstucePourquoi s’agit-il d’un fast-forward merge ? Tout simplement parce que les deux branches renouveau et origin/renouveau n’ont pas divergé depuis que Charles a ajouté une strophe.

Imaginons maintenant un autre cas de figure, où la fusion se complique un tout petit peu. Paul travaille sur son clone local, toujours sur la branche aurore :

$ cd ../clone-de-paul/
$ git switch aurore 
Switched to branch 'aurore'
Your branch is up to date with 'origin/aurore'.

Avant de continuer à travailler sur Aurore, il a l’idée de faire en parallèle une parodie de soi-même. Il crée un fichier Aurore-Parodie.md et l’édite comme ceci :

# Aurore (parodie)

La félicité sans bornes
De ronfler en ton giron
Se dissipe dès la morne
Apparence des klaxons.
Dans mon âme je recule,
Toutes mes idées se bousculent ;
C'est la première livraison !
À peine sorti des vapes,
Je me lève et je dérape
Sur le fil du téléphon.

AstuceNote du blogueur : cette parodie est de mon cru. En 1995 je m’étais inscrit à un cours terriblement ennuyeux sur Paul Valéry à la Fac de Lettres de Montpellier. Vu que les smartphones n’existaient pas encore à l’époque, je me suis amusé à ma façon.

Paul ajoute le fichier à l’index et effectue un commit :

$ git add Aurore-Parodie.md 
$ git commit -m "Aurore: Parodie."

Il publie ces modifications :

$ git push

C’est au tour de Charles. Il veut bien filer un autre coup de main à Paul, mais avant de faire quoi que ce soit, il va d’abord vérifier s’il y a du nouveau :

$ cd ../clone-de-charles/
$ git switch aurore 
Switched to branch 'aurore'
Your branch is up to date with 'origin/aurore'.
$ git fetch
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 508 bytes | 508.00 KiB/s, done.
From github.com:kikinovak/poesie-symboliste
   44d5975..a449645  aurore     -> origin/aurore
$ git status
On branch aurore
Your branch is behind 'origin/aurore' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Il y a du nouveau sur la branche origin/aurore. Il va donc fusionner cette branche :

$ git merge origin/aurore 
Updating 44d5975..a449645
Fast-forward
 Aurore-Parodie.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)
 create mode 100644 Aurore-Parodie.md

Charles voit apparaître la nouvelle version du poème :

$ ls
Aurore.md  Aurore-Parodie.md  README.md

Cette version n’est pas vraiment à son goût, et il préfère ajouter une strophe au poème original dans le fichier Aurore.md :

Salut ! encore endormies
À vos sourires jumeaux,
Similitudes amies
Qui brillez parmi les mots !
Au vacarme des abeilles
Je vous aurai par corbeilles,
Et sur l’échelon tremblant
De mon échelle dorée,
Ma prudence évaporée
Déjà pose son pied blanc.

Il ajoute cette modification à l’index et effectue un commit :

$ git add Aurore.md 
$ git commit -m "Aurore: 2ème strophe."

Il publie cette suggestion :

$ git push

Pendant que Charles est occupé à faire tout ça, Paul continue à travailler sur son clone local :

$ cd ../clone-de-paul/

Il se dit qu’il a oublié de signer la parodie. Il ouvre le fichier Aurore-Parodie.md et ajoute l’auteur :

# Aurore (parodie)

*Kiki Novak*
  
La félicité sans bornes
De ronfler en ton giron
Se dissipe dès la morne
Apparence des klaxons.
Dans mon âme je recule,
Toutes mes idées se bousculent ;
C'est la première livraison !
À peine sorti des vapes,
Je me lève et je dérape
Sur le fil du téléphon.

Il enregistre les modifications et effectue un commit :

$ git add Aurore-Parodie.md 
$ git commit -m "Aurore (parodie): ajout de l'auteur."

Malheureusement, la tentative de publication avec git push se solde par un échec :

$ git push
To github.com:kikinovak/poesie-symboliste.git
 ! [rejected]        aurore -> aurore (fetch first)
error: failed to push some refs to 'github.com:kikinovak/poesie-symboliste.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Une solution propre consiste ici à récupérer dans un premier temps les nouveautés dans les branches de suivi à distance sans pour autant perturber les branches locales :

$ git fetch
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), 1.19 KiB | 1.19 MiB/s, done.
From github.com:kikinovak/poesie-symboliste
   a449645..3b3a59c  aurore     -> origin/aurore
   45cb7ce..69736f3  renouveau  -> origin/renouveau

Où en sommes-nous ? Là encore, c’est git status qui va nous aider à nous repérer :

$ git status
On branch aurore
Your branch and 'origin/aurore' have diverged,
and have 1 and 1 different commits each, respectively.

Paul va tenter de fusionner les deux branches aurore et origin/aurore :

$ git merge origin/aurore

Cette fois-ci, Git affiche le message de fusion suivant :

Merge remote-tracking branch 'origin/aurore' into aurore

AstuceQue s’est-il passé ? Nous obtenons un merge commit pour la simple raison que les deux branches aurore et origin/aurore ont divergé.

Où en sommes-nous maintenant que la fusion a été effectuée avec succès ?

$ git status
On branch aurore
Your branch is ahead of 'origin/aurore' by 2 commits.
  (use "git push" to publish your local commits)

Il ne nous reste plus qu’à prendre Git au pied de la lettre et à rendre tout cela public :

$ git push

S’il ne fallait retenir que cela

Dans cet atelier pratique, j’ai mis la charrue de la pratique avant les bœufs de la théorie pour vous faire comprendre par l’exemple que la commande git pull n’est en somme que la combinaison de git fetch suivi d’un git merge. Autrement dit (on insiste un peu là) :

git pull = git fetch + git merge

AstuceVous aviez peut-être l’habitude d’utiliser git pull au quotidien. Le hic ici, c’est que dans certaines situations, beaucoup de choses se passent sous le capot de manière automagique. Le simple fait de remplacer git pull par la combinaison de git fetch et git merge permet de reprendre le contrôle sur tous les détails des opérations, notamment lorsqu’il s’agit de concilier des branches qui divergent un tant soit peu.

Exercice 1

  • Complétez petit à petit et strophe par strophe l’anthologie de la poésie symboliste.
  • Le texte intégral des trois poèmes est disponible librement sur le web.
  • Endossez successivement le rôle de Charles, de Paul et de Stéphane.
  • Ne vous découragez pas si vous vous tirez dans le pied.
  • Si votre dépôt est dans un piteux état, faites une pause et recommencez le lendemain.
  • À un moment, il va falloir intégrer tout ce travail dans la branche principale main.

Bonus : un peu de ménage

Une fois que nous avons tout intégré dans la branche main, il nous reste à faire le ménage dans les branches que nous n’utilisons plus. Vous savez comment supprimer une branche locale :

$ git branch -d aurore 
Deleted branch aurore (was caaec34).

La suppression d’une branche de suivi distant n’est pas tout à fait intuitive :

$ git push -d origin aurore 
To github.com:kikinovak/poesie-symboliste.git 
 - [deleted] aurore

La commande git fetch -p (ou --prune) permet de faire le ménage dans les branches qui n’ont plus de branche distante correspondante :

$ git fetch -p
From github.com:kikinovak/poesie-symboliste
 - [deleted]         (none)     -> origin/aurore

La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.

 

Catégories : Formation

0 commentaire

Laisser un commentaire

Emplacement de l’avatar

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