Contrôle de version avec git

git permet de faire du contrôle de version. La différence avec d'autres systèmes de gestion de version est son aspect distribué et son système de branche.

La première partie de ce document ressemble beaucoup à l'introduction à git que vous avez normalement eu en APL. Vous pouvez la relire très rapidement. De manière générale, le site officiel est une excellente source d'information.

La seconde partie contient plus de détails et des exercices pour aller plus loin, en particulier, comment à travailler à plusieurs et une introduction aux branches, qui est un aspect majeur de git.

Tout au long de ce TP, je vous invite à conserver une petite feuille de papier ou un fichier texte dans lequel prendre des notes pour lister chaque commande de git et une description indiquant à quoi elle sert.

À la fin de ce TP vous avez un quizz et une fin cocasse pour vous motiver à aller jusqu'au bout.

Digression : rsync

Notez que si vous travaillez seul, et si vous souhaitez juste synchroniser des arborescences il existe la commande rsync.

Du temps ancestral d'avant dropbox et autre client de nuage, pour travailler seul sans home sur un serveur mais sur plusieurs machines, il existait rsync, une commande qui permet de synchroniser une arborescence manuellement ou régulièrement et automatiquement avec un démon.

Il s'agit de «green IT» avant l'heure puisque rsync ne va pas transmettre intégralement la nouvelle version de l'arborescence mais va envoyer en gros seulement les petites différences de chaque fichier.

Inconvénient par rapport à git : on ne peut pas revenir à une version antérieure. Avantage par rapport à git : potentiellement beaucoup moins gourmand en bande passante.

git

Pour rappel, nous avons un serveur git au département.

Rendez-vous sur la page d'accueil de Gitea et identifiez-vous. NB : ce service acceptera les mêmes identifiants que les machines virtuelles proxmox.

Pour éviter de saisir votre identifiant et votre mot de passe à chaque fois que vous transmettez une commande au serveur, je vous invite à suivre les étapes ci-dessous, si ce n'est pas déjà fait.

Une fois identifié, accédez aux paramètres de votre compte et mettez à jour le nom complet et l'adresse e-mail principale. Nous allons nous assurer que git sur votre machine et Gitea sur le serveur ont bien les mêmes informations. Dans un terminal, lancez les commandes suivantes (en utilisant les mêmes informations que sur Gitea) :

git config --global user.name "Votre nom"
git config --global user.email "login@domaine.example"
git config --global core.editor vim

NB : si vous voulez utiliser un autre éditeur, vous pouvez tout à fait remplacer vim par emacs ci-dessus. (trivia voir editor war). C'est l'éditeur qui sera choisi en cas de besoin par git, par exemple si vous faites un commit sans donner l'option -m "un message", git vous demandera d'éditer le message en lançant un éditeur. Il vaut mieux donc choisir un éditeur que vous maîtrisez un minimum.

Pour sécuriser les échanges entre votre machine et le serveur, il va vous falloir une paire de clés de cryptage. Tapez la commande suivante et validez sans entrer de réponse si des questions vous sont posées :

ssh-keygen -t rsa -C "login@domaine.example"

Ceci a créé parmi vos fichiers une clé privée et une clé publique. Nous devons maintenant donner la clé publique à Gitea. Commençons par afficher la clé publique :

cat .ssh/id_rsa.pub

Vous pouvez copier cette clé en la sélectionnant à la souris (aucun raccourci clavier n'est nécessaire). Revenez ensuite dans le navigateur et parmi les paramètres de votre compte Gitea, choisissez Clés SSH. Cliquez sur Ajouter une Clé puis copiez la clé dans la boîte Contenu (en appuyant sur la molette de la souris). Choisissez le nom que vous voulez pour la clé (vous pouvez par exemple l'appeler TP si c'est la clé générée dans une machine des salles de TP).

Création d'un dépôt en ligne

Chaque projet auquel vous participez doit avoir un dépôt dédié (et même plusieurs, comme nous le verrons par la suite). Vous pouvez aussi avoir un dépôt dédié pour chaque cours, surtout si il y a des TPs.

Créez un nouveau dépôt en utilisant le bouton qui ressemble à un +.

Si c'est un projet, il faut probablement suivre une convention de nommage pour que le correcteur retrouve le projet facilement. Rendez-le privé. Donnez les accès adaptés au(x) correcteur(s) et autre(s) membre(s) de votre projet.

Dans la catégorie .gitignore vous pouvez choisir plusieurs modèles en fonction de votre projet et du type de fichiers qu'il va contenir (ou plus exactement de ceux que vous ne souhaitez pas sauvegarder).

Vous pouvez ensuite valider et constater que vous avez un dépôt contenant deux fichiers

  1. un fichier texte pour la license
  2. un fichier texte 'readme.md' qui devra à terme contenir un bref descriptif de l'architecture de votre projet.

Le dépôt en ligne est prêt et vous n'avez plus vraiment besoin d'accéder à Gitea par son interface web à partir de maintenant.

Cloner un dépôt localement

Pour cloner un dépôt localement sur votre machine, il vous suffit de regarder son adresse sur un serveur git et d'y avoir accès.

Par exemple, pour le dépôt du projet PouetPouet de l'utilisateur toto sur le serveur git de l'IUT, il faut saisir :

git clone gitea@dwarves.iut-fbleau.fr:toto/PouetPouet.git

S'il s'agit de votre dépôt que vous venez de créer, vous allez voir si vous regardez tous les fichiers y compris les fichiers cachés (par exemple en faisant ls -a) :

  • un répertoire .git
  • un fichier texte .gitignore
  • un fichier texte en markdown README.md

Le dépôt local à proprement parler est dans le répertoire .git. Vous n'avez pas besoin de le visiter, mais vous pouvez y jeter un coup d'oeil si vous êtes curieux. Le répertoire PouetPouet est le répertoire de la copie de travail des fichiers du dépôt.

Attention il faut éviter de cloner un dépôt git dans un dépôt git. C'est possible de le faire, mais c'est un mécanisme avancé de git.

Alternative : créer un dépôt local et le pousser dans un dépôt distant vide

Alternativement, vous pouvez utiliser git init pour créer un dépôt local. Ensuite il faut utiliser git add et git commit pour indexer des fichiers puis faire un point de sauvegarde local (comme expliqué ci-dessus).

Ensuite git remote add origin suivi de l'url du dépôt sur le serveur (NB : origin est le nom que git donne au dépôt du serveur distant par défaut ; il est parfois possible d'avoir plusieurs dépôts distants) pour indiquer le nom du dépôt distant.

Finalement il faut faire git push -u -f origin master (NB : master est le nom de la branche principale, ici celle du dépôt local, l'option -u va éviter d'avoir à répéter origin et master par la suite dans les commandes).

Utiliser Git au quotidien

Dans cette partie nous illustrons l'usage de git à travers quelques exemples et exercices.

Git en local

L'utilisation de base pour un travail seul en local avec git sans sauvegarde distante est la suivante :

  • travail sur un fichier local monFichier
  • ajout de ce fichier local dans l'index (git add)
  • point de sauvegarde (git commit)

Par exemple :

emacs monFichier
git add monFichier
git commit -m "un message court expliquant le changement de monFichier"

Si vous travaillez seulement en local, ceci suffit à avoir un système de version permettant de revenir en arrière.

Pour revenir en arrière sur 1 fichier il faut utiliser :

git restore --source <le numéro de version> monFichier

(NB : c'est l'analogue d'un rollback de base de données)

Pour connaître le numéro d'une version, vous pouvez utiliser git log.

Si vous êtes un peu perdu git status donne beaucoup d'information et suggère des commandes. Vous pouvez lire le man sur ces commandes ou regarder les docs en ligne.

Remarque git restore est relativement récent et avant il y avait git checkout. Cette seconde commande a beaucoup d'usages, ce qui contrevient à l'esprit unix une commande pour un usage. Sauf si vous maîtrisez bien git checkout, mieux vaut utiliser les diverses nouvelles commandes qui la remplacent, comme git restore et git switch.

Exercice 1. Dans un nouveau dépôt local git, créez un nouveau fichier git.md et inserez-y le contenu ci-dessous :

Git is a term of insult denoting an unpleasant, silly, incompetent, annoying, senile, elderly or childish person. As a mild oath it is roughly on a par with prat and marginally less pejorative than berk. Typically a good-natured admonition with a strong implication of familiarity, git is more severe than twit or idiot but less severe than wanker, arsehole or twat when offence is intended.

Sauvez le fichier. Lancez la commande git status. Indexez le nouveau fichier, puis créez un point de sauvegarde avec le message "git definition". Lancez la commance git log.

En cette période de jubilé de platine de sa majesté, il est difficile de laisser visible un tel texte. Nous allons maintenant chiffrer le contenu du fichier en ROT13, avec la commande :

sed -i 'y/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM/' git.md

Ensuite refaites un nouveau point de sauvegarde avec le message "vg qrsvavgvba".

Le jubilé étant passé et Bojo étant toujours premier ministre, on décide de revenir à la version antérieure. Restorez le contenu du fichier git.md.

Git en local et sur serveur distant

Avec un serveur distant, vous pouvez normalement garantir de conserver tout votre travail qui est sauvé sur le serveur git, même si votre ordinateur personnel vous lâche.

Si vous avez plusieurs machines/comptes, il faut faire un git pull pour récupérer la version du serveur.

Ensuite vous travaillez en local comme ci-dessus (cycle édition, git add, git commit).

Dès que vous voulez sauver sur le serveur, il faut faire un git push.

Remarque Vous n'êtes pas obligé de faire un push pour chaque commit local. Le commit local sert à faire des undo alors que le push sert à montrer des changements relativement conséquents (potentiellement utile pour d'autres lecteurs que vous le jour où vous travaillez à plusieurs). Voir cette question pour plus de détails.

Exercice 2. On reprend l'exercice précadent avec un fichier git2.md contenant 2 lignes de plus que précédemment.

Git is a term of insult denoting an unpleasant, silly, incompetent, annoying, senile, elderly or childish person. As a mild oath it is roughly on a par with prat and marginally less pejorative than berk. Typically a good-natured admonition with a strong implication of familiarity, git is more severe than twit or idiot but less severe than wanker, arsehole or twat when offence is intended. The term is used by Graham Chapman in the Monty Python sketch "Argument Clinic". The term is also frequently used by the character Derek 'Del Boy' Trotter in TV series Only Fools and Horses.

Indexer le fichier, sauver le fichier, faire un commit local. Faire du ROT13, indexer, sauver le fichier, faire un commit local.

Faire un push, lancer git log en local, aller voir les révisions sur l'interface web de Gitea.

Faire un restore en local. Regarder si il y a des effets sur le dépôt local. Regarder si il y a des effets sur le dépôt distant via l'interace web.

Le jubilé étant passé et Bojo étant toujours premier ministre, on décide de revenir à la version antérieure. Restorez le contenu du fichier git.md.

Nous travaillons à deux

Vous n'êtes pas obligé de travailler à deux pour cet exercice, ni même d'avoir deux machines clientes. Vous pouvez simuler la seconde personne en vous mettant dans un autre répertoire sur votre machine et en y clonant le dépôt.

Dans cet exercice nous simulons un conflit de version sur un fichier. En gros vous faites un git push et le message vous indique qu'il y a un conflit et comment le résoudre. Il faut faire un git pull. Git essaye de faire la fusion automatique et vous indique un conflit. Vous devez éditer le fichier qui contient maintenant très clairement les lignes divergentes de l'autre version. Vous faites votre selection, sauvez le fichier, l'indexer, commit puis pousser le changement.

Normalement ceci fonctionne assez bien pour des choses simples, en particulier si vous changez des lignes différentes d'un fichier.

Exercice 3. Ajouter à votre dépôt local les fichiers du précédent TP noté Memoire.java et MaMemoire.java.

Les indexer, faire un commit, pousser sur origin. Donner accès en écriture au dépôt distant à un collègue.

En parallèle sur vos machines respectives :

  • Le premier corrige les bugs de 'add' et 'contain' (avec commentaire BUGFIX etc).
  • Le second procède de même sur 'remove' et 'size'.
  • Les deux indexent et commit.

Puis dans cet ordre :

  • Le second pousse.
  • Le premier essaye de pousser.
  • Le premier résoud le conflit en faisant un git pull.
  • Le premier doit éditer MaMemoire.java pour avoir une version cohérente du fichier appliquant les changements des 2 personnes.
  • Le premier fait un git add, un git commit puis un git push.
  • Le second profite de ce travail en faisant un git pull.

L'étape d'édition est probablement inutile si vous avez changé des lignes différentes du fichier.

Exercice 4. Reprendre l'exo précédent, mais cette fois ajoutez chacun des changements sur des lignes communes. Par exemple vous changez chacun la javadoc de la même méthode ou bien vous changez le nom de la même variable dans la même méthode. Normalement la résolution automatique du conflit et la fusion des fichiers va échouer et vous allez être obligé de faire la fusion manuelle.

Les branches

La spécificité de git par rapport à d'autres système de contrôle de versions est que git encourage très fortement l'utilisation très fréquente de branches et que sous le capot il s'agit de quelque chose d'assez léger.

Pour fabriquer une branche, on va faire :

git branch LeNomDeMaBranche

Pour changer de branche, on peut utiliser git checkout mais je vous recommande la commande plus moderne qui ne sert qu'à changer de branche qui s'appelle git switch. Par exemple, pour basculer sur cette branche qu'on vient de créer, on va faire :

git switch LeNomDeMaBranche

Notez que vous aviez avant d'ajouter cette nouvelle branche une branche par défaut qui se nomme master.

Dans la suite je vais supposer que chaque branche est dans un état propre, à savoir tous les fichiers utiles ou changés sont indexés (git add) et il y a un point de sauvegarde local (git commit). Ceci permet de ne jamais perdre son travail quand on bascule de branche en branche.

Vous pouvez changer à tout moment de branche avec git switch.

Pour fusionner le travail d'une branche dans une autre branche, il faut vous placer dans la branche dans laquelle vous souhaiter injecter le changement et utiliser git merge. Par exemple, pour insérer les changements de LeNomDeMaBranche dans master il faut juste faire :

git switch master
git merge LeNomDeMaBranche

Si LeNomDeMaBranche est juste un successeur de master la fusion est très simple pour git puisque c'est juste du déplacement de pointeur (en gros master va pointer sur LeNomDeMaBranche). Dans son jargon, git évoquera un fast-forward. Si au contraire LeNomDeMaBranche et master ont un ancêtre commun car il y a eu des changements dans la branche principale master depuis que la branche LeNomDeMaBranche a poussé, alors la fusion est un peu plus compliquée à mettre en oeuvre, mais pourra probablement être faite automatiquement. Dans son jargon, git évoquera un three-way merge. Si git n'arrive pas à résoudre tout automatiquement, alors il va évoquer un merge conflict. Vous pouvez utiliser git status, puis changer à la main ce qu'il faut.

Si vous avez intégré le travail d'une branche qui ne sert maintenant plus à rien, vous pouvez l'effacer en faisant :

git branch -d LeNomDeMaBranche

Attention Ce n'est pas quelque chose qu'on peut annuler.

Le livre en ligne sur le site de git (disponible en plusieurs langues dont le français) est très bien fait et vous pouvez avoir plus de détails en regardant ici et .

Pour aller plus loin

Fin cocasse

Jeu en javascript pour s'entraîner

Quizz

  1. Qui a commencé le développement de git ?
  2. Comment les fichiers d'un dépôt git sont-ils indexés ?
  3. Faut-il un serveur git centralisé ?
  4. Quelle commande permet-elle d'indexer un fichier ?
  5. Quelle commande permet-elle de récupérer le dernier point de sauvegarde depuis le dépôt distant ?
  6. Que veut dire origin dans le contexte de git ?
  7. Que veut dire master dans le contexte de git ?
  8. Quelle commande permet-elle de transmettre le dernier point de sauvegarde dans le dépôt distant ?
  9. Comment connaître le nom des points de sauvegarde ?
  10. Comment savoir où on en est quand on ne sait plus trop quoi faire ?
  11. What is the difference between a git and a plonker?

retour à la page d'accueil

retour au sommet