Révisions GIT: épisode 4

Les 3 premiers épisodes de notre série, se focalisaient sur les dépôts locaux, dont chaque développeur dispose sur sa machine. Nous allons découvrir maintenant les dépôts centraux / distants (central / remote repositories), comprendre ce que l’on peut en attendre, et voir les quelques commandes supplémentaires qu’il faut connaître.

Révisions GIT: épisode 4

Pourquoi un dépôt central / distant ?

Jusqu’à présent, nous nous sommes toujours placés du point de vue d’un développeur individuel. Nous étions sur une machine avec 1 ou plusieurs dépôts locaux. Cependant ces dépôts ne répondent pas à toutes les questions, notamment les questions suivantes:

  • Comment je travaille en équipe, avec d’autres développeurs ? Collaboration
  • Comment je partage mon code, avec ma communauté ? Partage / Publication

La réponse à ces deux questions ne peut, en effet, pas être donnée par des dépôts locaux. Il faut un point de référence commun aux différents développeurs. C’est donc le rôle des dépôts distants. Avoir un dépôt distant permet, en outre, de disposer d’une sauvegarde de notre code.

Il faut bien différencier deux notions

  • Le serveur GIT proprement dit (git server), qui est une application s’exécutant sur un serveur, et capable de dialoguer avec les git clients,
  • Les services git (git server as a service), qui sont des plateformes internet, implémentant la fonction git serveur, en lui ajoutant des fonctionnalités complémentaires, comme la gestion des tickets, la gestion de projets, de tableaux de bord Kanban, etc.

Un git server peut s’installer n’importe où, y compris sur notre propre machine si nous le souhaitons. Si vous disposez d’un NAS Synology, git server fait partie des composants installables.

Figure 1: Git server sur un Synology
Figure 1: Git server sur un Synology

Certaines enterprises installent leur propre serveur GIT, pour héberger du code « sensible ».

Mais un serveur GIT n’offre pas rien de plus que ce que nous avons sur notre machine, et l’ergonomie est la même. Les services GIT, quand à eux, apportent les fonctionnalités d’un serveur GIT, en y ajouant des outils complémentaires :

  • une interface ergonomique (en principe),
  • un gestionnaire de ticket,
  • une gestione de flux d’approbation de code,
  • … …

Lorsque l’on parle GitHub, ou Gitlab nous parlons de service GIT. Il existe de multiples plateformes similaires sur internet, comme SourceForge, Framagit, et plus d’une trentaine d’autres 1

Dans la suite de l’épisode, nous parlerons de service GIT, et les exemples seront basés sur GitHub (parce que c’est celui que j’utilise)

Créer le lien local / distant

Commençons par le plus simple, avec la commande git remote.

Si nous voulons voir les dépôts distants liés à nos dépôts locaux:

$ git remote [-v]

Figure 2: Situation initiale
Figure 2: Situation initiale

Si nous voulons relier un dépôt local à un dépôt distant (existant)

$ git remote add <nom court> <url vers le dépôt distant>
$
$ git remote add blog https://github.com/egeorjon/EG-Blog.git

Vérifions ce que cela donne

$ git remote
    blog

$ git remote -v
    blog	https://github.com/egeorjon/EG-Blog (fetch)
    blog	https://github.com/egeorjon/EG-Blog (push)

Figure 3: Dépôt distant relié
Figure 3: Dépôt distant relié

Le résultat de la seconde commande montre les deux flux que GIT met en place lorsque l’on ajoute un dépôt distant

  • Le flux descendant (du distant vers le local): fetch
  • Le flux montant (du local vers le distant): push

Comme je l’ai expliqué dans le second épisode, créer un dépôt localement, puis le relier à un dépôt distant, n’est pas une méthode conseillée. La méthode généralement décrite est

  • Je crée un dépôt sur un outil comme GitHub ou Gitlab. Je choisis différentes options comme la licence,
  • Je crée mon dépôt local à partir du dépôt distant, avec la commande git clone.

Figure 4: Zone locale et dépôt distant avant clonâge
Figure 4: Zone locale et dépôt distant avant clonâge

$ git clone <url vers le dépôt distant> [<nom court>]
$ 
$ git clone https://github.com/egeorjon/EG-Blog.git blog

Si le nom court n’est pas spécifié, le nom court par défaut sera origin. L’avantage de cette méthode est que vous êtes sûre que vous démarrez avec des dépôts qui sont complètement alignés.

Figure 5: Dépôts local et distant après clonâge
Figure 5: Dépôts local et distant après clonâge

Le lien local / distant peut être modifié / supprimé

$ git remote rename <ancien nom court> <nouveau nom court>   # Changer le nom court du dépôt distant
$ 
$ git remote rm <nom court> # Supprime le lien vers le dépôt distant.

Push / fetch / pull

Les dépôts centraux ajoutent 3 commandes principales:

Figure 6: Les commandes liées aux dépôts distants
Figure 6: Les commandes liées aux dépôts distants

La commande push

Cette opération consiste à pousser (upload) les commits d’une branche vers une branche distante, ou plus exactement, elle permet de mettre à jour les branches distantes.

Partons de la situation de la figure 7

Figure 7: Etat des dépôts avant le push
Figure 7: Etat des dépôts avant le push

La commande accepte de multiples paramètres:

$ git push   # est équivalent à git push origin <branche courante>
$ git push <dépôt distant> <branche>   # Pousse la branche spécifiée
$ git push <dépôt distant> --all    # pousse toutes les branches

Figure 8: Etat des dépôts après le push
Figure 8: Etat des dépôts après le push

La notion de fast-forward (avance rapide)

Certains d’entre vous pensent probablement que git push est finalement un git merge vers une branche distante. C’est tout à fait vrai, avec une restriction cependant: git push n’effectue que des fast-foward.

Comme expliqué dans le chapitre 3, lorsqu’une opération conduit à déplacer le pointeur d’un commit A, vers un autre commit B, cette opération est appelée avance rapide (fast-forward), si et seulement si B est un descendant de A.

Figure 9: Le fast-forward expliqué
Figure 9: Le fast-forward expliqué

Dans le cas de la figure 9, John ne peut pas faire de push car C dépend de B, et non de D. Dans ce cas, John a deux solutions:

  • Forcer le push, et écraser le dépôt central,
  • Ou utiliser la commande fetch / merge ou pull, pour ré-aligner son dépôt local avec le dépôt distant, avant de faire de nouveau le push (voir paragraphe suivant).

Forcer le push s’effectue avec la commande git push --force. Cette méthode n’est pas conseillée: si elle résoud le problème du point de vue de John, elle ne fait que reporter le problème sur Annabelle, et elle peut engendrer des pertes de code.

Figure 10: Utilisation de git push --force
Figure 10: Utilisation de git push --force

La commande fetch

La commande git fetch va récupérer l’ensemble des modifications correspondant à une branche ou un tag d’un dépôt distant, ne figurant pas dans le dépôt local. Une fois la commande passée, le dépôt local contiendra les références du dépôt distant, nous permettant ainsi de pouvoir réaliser des fusions.

La syntaxe est la suivante:

$ git fetch <nom court du dépôt distant> <référence>  # la référence peut être une branche, ou un tag.
$ git fetch <nom court du dépôt distant> # récupère l'intégralité du dépôt distant

Figure 11: Situation avant le fetch
Figure 11: Situation avant le fetch

La commande fetch ne modifie pas le dépôt local, elle

  • Télécharge les branches ou les références distantes,
  • Indique les différences détectées (entre les branches locales, et les branches téléchargées).

Les modifications des branches ou références distantes sont stockées dans le dépôt local, et sont accessibles via le nom <nom court du dépôt distant>/<nom de la branche> (par exemple origin\devA).

Figure 12: La commande fetch
Figure 12: La commande fetch

Pour réellement incorporer les modifications, il faut utiliser la commande git merge

$ git checkout ...
$ git merge ...

Figure 13: Situation après les commandes fetch et merge
Figure 13: Situation après les commandes fetch et merge

Si l’on reprend le cas de John, la façon « propre » de résoudre son problème sera donc la suivante:

Figure 14: Enchaînement des commandes fetch / merge et push
Figure 14: Enchaînement des commandes fetch / merge et push
.

D’une manière générale, une session de travail devrait toujours commencé par un fetch, et se terminer par un push

La commande pull

Globalement la commande pull est une combinaison des deux commandes fetch et merge.

Figure 15: Situation avant la commande pull
Figure 15: Situation avant la commande pull

$ git pull <nom court du dépôt distant> [--verbose]
$
$ # this command is equivalent to
$ git fetch <nom court du dépôt distant>
$ git merge origin/<branche active>

Figure 16: Situation après la commande pull
Figure 16: Situation après la commande pull

Au premier abord git pull semble être une commande efficace, mais comparée à git fetch, elle est un plus risquée.

  • git fetch se contente de télécharger les branches distantes, et de signaler les différences avec les branches locales.
  • git pull agit sur le dépôt local, en effectuant une fusion. Il y a donc des risques de conflits.

Une histoire de processus

Nous venons de parcourir les quelques commandes nécessaires à la synchronisation d’un dépôt local, avec un dépot distant. Pour terminer cet article, je vais laisser de côté les aspects techniques, et parler « méthodologie ».

Pour moi, travailler avec GIT nécessite de répondre à une contradiction

  • D’un côté nous avons la flexibilité de l’outil lui-même, grâce au très grand nombre de ces fonctionnalités,
  • D’un autre côté, l’outil aussi souple qu’il soit, nécessite une certaine rigueur de travail, rigueur sans laquelle, nous pourrions perdre du code, ou passer notre temps à corriger les erreurs d’aiguillage, et cette rigueur peut passer pour une contrainte, et un manque de flexibilité.

La façon de résoudre ces contractions est de définir le flow de développement, surtout si vous travaillez à plusieurs sur le même projet. Ce flux de développement doit décrire le mode de fonctionement entre les développeurs, et avec les conséquences sur le processus de travail de chaque développeur.

J’illustre ce que je viens de dire avec le cas de ce blog

  • Le blog prend la forme d’un ensemble de fichiers Markdown, et d’un thème.
  • Le tout est géré dans une dépôt sur mon PC. Le dépôt distant se trouve sur GitHub,
  • Quand je fais un pull request, la plateforme Netlify génère le site en mode « preview »,
  • Quand je fais un merge (sous-entendu d’une branche avec la branche main), la plateforme Netlify génère le site en mode « production »

Je précise que je connaissais GitHub avant de démarrer, mais sans le pratiquer quotidiennement.

A partir de là, il serait trop long de lister toutes les problèmes que j’ai rencontré au début. En voici, malgré tout, quelques-uns:

  • Commits effectués sur la mauvaise branche,
  • Ne me demandez pas pourquoi, mais je faisais certains développements directement dans main (commme le thème par exemple), et d’autres dans des branches,
  • Donc cela veut dire que je faisais un push de la branche main, donc Netlify me générait directement la version de production, qui plantait parfois,
  • Ajout d’articles ou modification d’articles, dans GitHub (le dépôt distant), puis en local, générant des conflits lors d’un push.
  • … …

Le point commun à tous ces problèmes: Je n’avais pas réfléchi précisement à la façon d’utiliser l’outil.

  • Est-ce que je rédige depuis un point unique ou non ?
  • Quelle doit-être ma structure de branches ?
  • Quelle méthode dois-je employer pour les modifications structurelles du site ?

Je vous recommande de lire l’article Contribution à un projet du livre en ligne Pro GIT. Vous verrez apparaître, au fil des lignes, des processus / rituels du type

  • Début de session,
  • fetch
  • diff
  • merge
  • edit / commit
  • edit / commit
  • … …
  • edit / commit
  • push
  • Fin de session (et on recommence).

C’est ce type de schéma-là qu’il faut avoir en tête, autant que la syntaxe des commandes git. Je pense que cela peut paraître évident à tous ceux qui pratiquent GIT depuis leur début de développeurs, mais ça l’est beaucoup moins pour les autres …


  1. Comparison of source-code-hosting facilities. Wikipedia, The Free Encyclopedia. April 14, 2021, 19:10 UTC. Available at: https://en.wikipedia.org/w/index.php?title=Comparison_of_source-code-hosting_facilities&oldid=1017817240. Accessed April 21, 2021. ↩︎

Commentaires