Système de gestion de version (git)

Jean-Jil Duchamps - cours basé sur des slides de Gérald Géniaut

M1 Modélisation Statistique, 2023-2024

Plan

  1. Présentation générale des VCS
    1. Objectifs
    2. Fonctionnement
    3. Les différents systèmes existants
  2. Git
    1. Brève histoire
    2. Git vs le monde
    3. Fonctionnement
    4. Les remotes
    5. Les branches
    6. Autres mécanismes

Référence

Le livre (gratuit, ouvert, en ligne) Pro Git par Scott Chacon et Ben Straub.

Avant tout

Si vous ne l’avez pas fait, installez et paramétrez Git sur votre propre ordinateur (cf. les deux diapos suivantes) !

Installer Git

Pour Windows, télécharger la dernière version proposée et l’installer :

  • Cliquer Next 4 fois, jusqu’à « Choosing the default editor … »
  • IMPORTANT : sélectionner « Use Notepad as Git’s default editor »
  • Cliquer Next pour toutes les autres options, puis Finish.

Pour Mac, installer Homebrew, puis depuis un terminal :

brew install git

Pour Linux, installer le paquet git

Paramétrer Git

Git utilise Vim comme éditeur par défaut
→ si vous trouvez ça dur, vous pouvez changer ce paramétrage :

Pour Windows, si vous avez bien suivi les instructions ci-dessus, tout va bien. Sinon, depuis le terminal Git (Git Bash), entrer :

git config --global core.editor notepad

Pour Mac, depuis un terminal :

git config --global core.editor "open --wait-apps --new -e"

Pour Linux (remplacer gedit par votre éditeur préféré) :

git config --global core.editor "gedit --wait --new-window"

Présentation générale des VCS

Objectifs d’un système de gestion de version (VCS)

NB: dans ce cours, on utilise l’abréviation anglaise VCS pour Version Control System pour coller avec une dénomination actuelle.

Pour n’importe quel projet :

  • remonter l’historique des modifications
  • savoir quelle est la version à une date donnée
  • connaître les différences entre 2 versions
  • travailler sur un aspect expérimental sans modifier la version stable du projet
Travail en local

Travailler en collaboration :

  • savoir qui a fait telle ou telle modification
  • fusionner des travaux en parallèle
Collaboration

Autres fonctionnalités :

  • Sécurité informatique : intégrité, disponibilité, confidentialité, traçabilité et non-répudiation
  • Tags : donner un nom à une version pour pouvoir la retrouver facilement
  • Branches : gérer un même projet en parallèle : production, test, dev

Fonctionnement

Principe d’un VCS

Problématique du travail à plusieurs

Alice et Bob accèdent au même fichier et le copient en local sur leur poste
Alice et Bob effectuent des modifications chacun·e de leur côté
Alice écrit sur le dépôt
Bob veut écrire sur le dépôt mais il risque d’écraser les modification d’Alice !

Solution : la fusion de versions

Bob met à jour, il récupère la version du dépôt sans perdre ses modifications

Un VCS gère le mécanisme de lecture-fusion-écriture:

  • Les demandes de lecture, écriture se font via le VCS
  • La fusion automatique est possible si les modifications ne touchent pas aux même contenus
  • Le VCS conserve l’historique
  • Cet historique est classifié : branches, tags, etc…

Les différents VCS existants

Les deux grandes catégories de VCS

  • Les systèmes centralisés
  • Les systèmes décentralisés

Les systèmes centralisés

Système centralisé

Avantages

  • Technologie éprouvée

Inconvénients

  • Impossible d’échanger entre les dépôts
  • Impossible d’échanger entre les copies locales
  • Impossible de travailler en mode « déconnecté »
  • Lent et lourd pour la mise à jour de gros projets
  • Pas de redondance

Ces inconvénients et les limites de ce système de VCS ont conduit à la création des systèmes décentralisés.

Les systèmes décentralisés

Système décentralisé

Conçus pour corriger les limites des systèmes centralisés :

  • Pouvoir utiliser le VCS en mode déconnecté
  • Ne pas être dépendant d’un dépôt centralisé (panne, temps, connexion, …)
  • Pouvoir échanger ses fichiers avec une partie des autres personnes travaillant sur le projet
  • Chaque personne dispose de son propre dépôt (donc tout un historique) en plus de sa copie de travail

Les avantages d’un VCS local

Chaque personne participant au projet dispose de son propre dépôt et de sa copie de travail.

Différents dépôts peuvent communiquer

  • clone d’un dépôt vers un autre (conserve l’historique)

  • écriture/lecture d’un dépôt vers l’autre

Dépôt central dans un VCS décentralisé

Utile pour stocker la version la plus à jour du projet, tout l’historique, différentes branches (production, développement, test)…

Système décentralisé

Les différents VCS du marché

  • 1972 : SCCS
  • 1985 : RCS
  • 1986 : CVS
  • 1997 : Code Co-op (premier VCS décentralisé)
  • 2000 : Bitkeeper, Subversion (ou SVN)
  • 2001 : arch
  • 2003 : Darcs
  • 2005 : Bazaar, Git, Mercurial
  • 2006 : Azure DevOps Server (Microsoft)

Ceux que l’on est le plus susceptible de croiser : Git (de loin), SVN et Mercurial.

Comment faire son choix ?

  • Pérennité : systèmes leaders vs. nouveaux systèmes
  • Intégré dans des IDE
  • Proposés par des forges
  • Interfaces graphiques
  • Portabilité
  • Sécurité
  • Documentations
  • Grande communauté

Conclusion

Utiliser un VCS :

  • C’est indispensable lorsque l’on travail (programme) en équipe
  • C’est la sécurité et l’efficacité (même quand on travail seul·e sur un projet)
  • Demande un effort d’utilisation très faible, et ce même sans interface graphique

Exemple d’utilisation de VCS

Ajout d’une classe Chance à votre projet de Monopoly en C++.

  1. Première série de modifications pour créer le header et le fichier source.
    ⇒ premier commit dans Git : "Création de la classe Chance".
  2. Ensuite, vous améliorez l’implémentation.
    ⇒ 2ème commit : "Nouvelle action de la case Chance".
  3. Enfin, vous corrigez un bug apparu avec votre implémentation.
    ⇒ 3ème commit : "Correction bug Chance".

La somme de tous les commits constitue l’historique de votre projet.

Intérêt :

  • Se placer à n’importe quel endroit de cet historique
  • Utile en cas de bug
  • Expérimenter, revenir en arrière, sans jamais perdre de travail
  • Habitude indispensable pour tout·e développeur·se

Git

Brève histoire

  • 2002 : le scandale initial. Linus Torvalds décide d’utiliser BitKeeper, VCS décentralisé mais logiciel propriétaire (gratuiciel – freeware) pour le développement du noyau Linux
  • 2005 : ce qui devait arriver. BitMover annonce la fin de la version gratuite de BitKeeper.
  • Dans la foulée, Git est développé par Linus Torvalds (et al.) pour remplacer BitKeeper dans le projet Linux
  • 2016 : Git domine. En 2016, Git est le VCS le plus populaire, utilisé par plus de 12 millions de personnes.

Git vs le monde

Une innovation de rupture : le stockage des données

Les autres VCS (SVN, etc.) gèrent l’historique comme une suite de modifications
Git stocke des instantanés de chaque version et optimise quand nécessaire → plus efficace

Les points clés (généraux)

  • Logiciel libre
  • Muti OS : Linux, Windows, MacOS, …
  • Des interfaces graphiques : https://git-scm.com/downloads/guis/
  • Très répandu : documentation abondante, grande communauté, beaucoup d’aide en ligne
  • Répandu, non seulement dans le monde du libre mais également en entreprise
  • Sécurisé : SSL, HTTPS

Les points clés (du VCS)

  • Décentralisé
  • ⇒ Presque toutes les opérations sont locales
  • Intégrité des fichiers : toute modification d’un fichier ou d’un répertoire est détectée par Git ⇒ garde-fou
  • En général, Git ne permet que d’ajouter de l’information ⇒ pas de perte de travail, tout l’historique est conservé, même lors des retours en arrière

Fonctionnement

  • on travaille dans le répertoire de travail (créations ou modifications de fichiers)
  • on indexe les changements (avec git add)
  • on commit l’index (enregistre une nouvelle version dans l’historique)
Répertoire de travail, zone d’index et répertoire Git

Principales commandes

  • git clone : copier un dépôt distant
  • git init : créer un dépôt local Git
  • git add : indexe un élément (l’ajoute au prochain commit)
  • git commit : enregistrer des modifications sur le dépôt local
  • git checkout : permet de se déplacer dans l’historique
  • git pull : tirer l’historique du dépôt distant vers le dépôt local
  • git push : pousser les modifications du dépôt local vers le dépôt distant

Les états des fichiers : non suivi, validé, modifié, indexé

Cycle de vie des fichiers avec Git
  • git add : indexe un élément (pour des fichiers non suivis ou modifiés)
  • git commit : enregistre les modifications (indexévalidé)
  • git rm : supprime un fichier de l’ordinateur et pour Git
  • git rm --cached : supprime un fichier pour Git (→ non suivi)
  • Non suivi : fichier que Git ignore
  • Validé : fichier dont l’ajout ou la modification a été validée par un commit
  • Modifié : fichier modifié dans le répertoire de travail, ni indexé, ni validé
  • Indexé : fichier modifié, marqué pour faire partie du prochain commit

Culture générale

Git reconnaît les modifications en calculant le SHA-1 (prononcé shawane) des fichiers – une fonction de hashage cryptographique qui transforme n’importe quelle donnée informatique en un mot hexadécimal de longueur 40.

$ echo test > test.txt
$ sha1sum test.txt
4e1243bd22c66e76c2ba9eddc1f91394e57f9f83  test.txt

En interne, les SHA-1 sont utilisés pour identifier tous les objets de Git (fichiers, commits, branches, etc.)

En pratique, on utilise le SHA-1 d’un commit comme son identifiant.

Vocabulaire

  • Chaque commit (version du projet) pointe vers son parent (version précédente) pour constituer l’historique
  • HEAD désigne l’endroit où l’on travaille dans l’historique des commits
Commits, main, HEAD, tags
  • main désigne en général la branche principale du projet (la dernière version à jour)
  • On peut décorer un commit avec une étiquette (ou tag) pour mieux repérer à quoi il correspond, ex : v1.0
Commits, main, HEAD, tags

L’historique

En ligne de commande :

  • git log affiche l’historique

  • git checkout permet (entre autres fonctionnalités avancées) de se déplacer dans l’historique

  • par exemple, git checkout HEAD~2 permet de mettre le répertoire de travail dans l’état de l’avant-dernier commit (« tilde » = « moins »)

En résumé

Un VCS permet de :

  • Travailler à plusieurs
  • Historiser les versions de ses documents :
    • Annoter (tag) une version
    • Revenir à une version antérieure (checkout)
  • Gérer plusieurs versions en parallèle
  • Deux types de VCS : centralisé (SVN, la méthode historique) et décentralisé (Git, la méthode moderne)

Mise en garde

Les systèmes de gestion de version sont faits pour fonctionner avec des fichiers textes : code, fichier Latex, Markdown…

Un peu de pratique (exo 1)

Tout d’abord (on ne fait ça qu’une fois) :

  1. Configurer son nom et son adresse mail avec git config (cf. la section 1.6 du livre Pro Git).

Ensuite :

  1. Suivre les instructions de l’exo1.md (sur GitLab).

NB: pour chacune des commandes, l’aide se trouve sur le site de Git, ou alors en utilisant git [commande] --help. Dans les deux cas, il y a beaucoup (trop) d’information… → allez voir les exemples !

Avant de manipuler plus de commandes, voyons comment mettre en place un dépôt distant central avec GitLab.

Les remotes (dépôts distants) et GitLab

L’historique sur son propre ordinateur c’est bien mais un dépôt distant (sur un serveur) c’est mieux :

  • Pour faire des sauvegardes de son travail (si votre ordinateur plante…)
  • Pour travailler en collaboration
  • Accessible depuis n’importe quel ordinateur

Hébergement de dépôts sur GitLab

Pourquoi GitLab et pas GitHub dans ce cours :

  • Les deux fonctionnent de manière (presque) identique.
  • GitLab, même en version gratuite, permet de créer des dépôts privés, pour s’entraîner sans complexe.
  • GitHub a été racheté par Microsoft, le code est plus fermé…
  • GitHub est plus couramment utilisé, mais après votre apprentissage sous GitLab, vous n’aurez aucun problème à vous y lancer.
  • D’autres alternatives intéressantes existent (Gitea, SourceForge, etc.), à découvrir par soi-même…

Vocabulaire

  • Projet ou repository : un dépôt sur le serveur.
  • Merge request (GitLab) ou pull request (GitHub) : Après avoir cloné un projet et fait vos propres modifications sur votre dépôt personnel, une merge/pull request permet de proposer d’intégrer vos modifications au dépôt originel.
  • Issue : espace de discussion des utilisateurs avec les développeurs autour d’un bug ou d’une demande de nouvelle fonctionnalité.

Cloner un dépôt

  • Depuis la page d’un dépôt, cliquer sur clone
  • Copier le lien (HTTPS, ou SSH pour vos propres projets)
  • Sur sa machine, depuis votre dossier de travail, taper :
git clone [lien copié]

Ceci crée un dossier nommé avec le nom du dépôt. Ce dossier est votre dépôt local, qui est une copie du dépôt distant (et donc avec tout l’historique, etc.).

Créer un compte GitLab

  • Créer son compte depuis ce lien, ou depuis la page d’accueil de GitLab : « Login > Register ».
  • Renseigner une adresse mail que vous consultez, et préférablement votre adresse étudiante (vous pourrez changer cette adresse par la suite, en rajouter, etc.).
  • Votre compte répertorie les projets hébergés par GitLab auxquels vous contribuez. On peut :
    • Communiquer avec d’autres personnes et signaler des problèmes de code en déclarant des issues
    • Copier des projets (fork) sur votre espace GitLab
    • Proposer des modifications de code à d’autres repos en faisant des merge requests (à partir de vos forks)

Avancé : ajouter sa clé SSH pour s’identifier rapidement

Dans un terminal :

  • Taper ssh-keygen -t [type de clé] (le type est un algorithme cryptographique qui doit être actuel pour être considéré sûr : choisir ed25519 par exemple)
  • Suivre les instructions :
    • Taper sur Entrée pour valider l’emplacement par défaut
    • Choisir un mot de passe (simple, vous allez l’utiliser souvent dans la suite)

Deux fichiers sont créés :

  • le fichier .pub correspond à la clé publique (une serrure)
  • l’autre à la clé privée (la « vraie » clé, à ne pas divulguer)

Depuis son compte GitLab, dans « Preferences > SSH Keys », copier le contenu de la clé publique.

Créer son dépôt

  • Cliquer sur le « + » en haut de l’interface GitLab
  • Choisir « New project/repository »
  • « Create blank project »
  • Renseigner le nom, une description optionnelle
  • Choisir « public » ou « privé »
  • On peut décocher l’option « Initialize project … »
  • Valider

Note : on peut aussi directement créer un dépôt distant depuis un dépôt local.

Manipuler un remote (exos 2 et 3)

  • Suivre les instructions de l’exo2.md (sur GitLab).
  • Suivre les instructions de l’exo3.md (sur GitLab).

Pensez à noter ce que fait chaque nouvelle commande :

  • git push
  • git pull
  • git status
  • git clone
  • git remote add ...
  • git branch main --set-upstream-to=...

Résumé

Manipulations locales

  • git init : initialiser un dépôt local
  • git add monfichier : ajouter ou indexer un fichier
  • git commit : valider des modifications
  • Important : mettre des commentaires (courts) sur ses commits.
  • Méthode rapide : git commit -m "mon commentaire"

Manipulations distantes

  • git clone : cloner un dépôt distant
  • git push et git pull : synchroniser son dépôt
  • git remote ... : ajouter ou supprimer des remotes

Les branches

  • Permettent de travailler sur des versions du code qui divergent de la branche principale.
  • Pour Git, une branche est simplement un pointeur vers un commit.
Trois branches

Utilité

  • Utile pour expérimenter sans modifier la version stable (qui fonctionne) du code.
  • Pour se concentrer sur le développement d’une fonctionnalité spécifique
Développement parallèle

Créer des branches, changer de branche

  • Branche initiale : main
  • git branch : montre les branches existantes (avec une astérisque devant la branche courante). Exemple :
$ git branch
* main
  developpement
  • git branch -v : affiche plus d’informations
$ git branch
* main        c6ba96f modifie README.md
  developpement 7d872f0 ajoute test pour is_polynome()
  • git branch ma_branche : crée une nouvelle branche
  • git checkout ma_branche : bascule sur la branche

Lors d’un checkout, le répertoire de travail doit être propre (pas de fichiers modifiés).

  • git checkout -b ma_branche : crée la branche ma_branche et bascule dessus

Fusionner (merge) des branches

Situation : on a travaillé sur la branche iss53 (qui cherche à résoudre l’issue 53) et abouti à des modifications que l’on voudrait intégrer à la branche main.

Procédure de fusion (merge) de base :

  • On se place sur la branche main :
git checkout main
  • Puis on utilise la commande git merge :
git merge iss53

Situation simple (fast-forward)

La branche main est en amont de iss53.

Situation de merge en fast-forward Résultat : main pointera simplement vers C3, aucun commit n’est rajouté.

Situation générale (3-way merge)

La branche main a suivi son chemin en parallèle de iss53.

Situation de 3-way merge

Résultat : un commit de fusion

Résultat d’un 3-way merge

Résoudre un conflit

Situation de conflit quand les mêmes lignes d’un fichier sont modifiées dans les deux branches à fusionner.

Exemple :

  • La branche main contient un fichier coucou.txt avec une ligne de texte : Hello world!
  • La branche iss53 contient un fichier coucou.txt avec une ligne de texte : Bonjour tout le monde...

Quand on tente de fusionner iss53 dans main :

$ git merge iss53
CONFLICT (add/add): Merge conflict in coucou.txt
Auto-merging coucou.txt
Automatic merge failed; fix conflicts and then commit the result.

Le commit de merge n’a pas pu être automatiquement créé.

Git entre alors en mode résolution de conflits. Extrait de git status :

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
    both added:      coucou.txt

Il faut alors :

  • Soit mettre coucou.txt dans un état convenable, l’ajouter et le valider.
  • Soit annuler la fusion (si le conflit est trop dur à gérer, etc.) avec git merge --abort

Pour l’exemple, on va éditer le fichier coucou.txt. Il se présente comme ça :

<<<<<<< HEAD
Hello world!
=======
Bonjour tout le monde...
>>>>>>> iss53

Ces lignes <<<<<<<, ======= et >>>>>>> délimitent les différences qu’il y a entre les deux branches.

On peut alors comparer les deux versions directement, choisir celle que l’on garde ou encore modifier complètement le fichier, par exemple :

Hello le monde !

Puis on indexe (git add coucou.txt), et on valide (git commit).

Un peu de pratique (exo 4)

Suivre les instructions de l’exo4.md (sur GitLab).

Autres mécanismes de Git

Afficher les différences

  • git diff affiche les différences entre les fichiers modifiés et leur état dans le dernier commit (plus exactement, leur état dans la zone d’index).
  • git diff ma_branche affiche les différences entre la branche courante et la branche ma_branche
  • git diff main...ma_branche (les trois points font partie de la syntaxe) affiche les différences entre ma_branche et l’ancêtre commun entre ma_branche et main.

Des outils pour faciliter les fusions

Si un « outil de fusion » (merge tool) est installé, on peut l’utiliser pour faciliter la résolution de conflit.

  • Installer un de ces outils (par exemple Meld sous Windows ou Linux)
  • Configurer git config --global merge.tool meld (remplacer meld par opendiff sous Mac – il devrait être installé avec XCode)
  • Lors d’un conflit, lancer git mergetool

Retrouvez qui a fait une modification

  • Faire une recherche en examinant les résultats de git log est compliqué !
  • La commande git blame est là pour ça :
    git blame coucou.txt affiche chaque ligne du fichier coucou.txt en identifiant le commit qui a modifié la ligne en dernier. Exemple :
git blame coucou.txt
d1ddcec8 (Toto Lafleur 1997-12-25 12:30:00 +0100 1) Hello le monde !
  • Le début du SHA-1 correspondant est affiché, puis
  • le nom de la personne responsable du commit, et
  • la date et l’heure du commit.

Pour un gros fichier, pour n’afficher que les lignes 42 à 56, utiliser git blame -L 42,56 gros_coucou.txt.

Pour retrouver pourquoi la modification a été faite, on peut :

  1. Utiliser git log et chercher le commit dont le SHA-1 commence par d1ddcec8.
  2. (mieux) Utiliser la commande git show d1ddcec8 : renvoie directement les détails du commit recherché.

Point essentiel : écrire des messages clairs et précis lorsqu’on commit.

Ignorer des fichiers : .gitignore

Pour des raisons de sécurité et de clarté, il est important d’ignorer certains fichiers dans Git, tels que :

  • Fichiers de configuration (config.xml, databases.yml, .env…)
  • Fichiers et dossiers temporaires (tmp, temp/…)
  • Fichiers « inutiles » comme ceux créés par votre IDE ou votre OS (.DS_Store, .project…)
  • Fichiers de compilation (.o, .exe…)

On peut dire à Git d’ignorer ces fichiers, sinon la commande git status affichera une (potentiellement longue) liste de fichiers présents mais non indexés…

Plusieurs méthodes :

  1. Modifier le fichier .git/info/exclude dans le dossier .git à la racine du dépôt.
  2. Créer un fichier .gitignore dans un dossier contenant des fichiers à ignorer.
  3. Créer un fichier où vous voulez servant de « .gitignore » global, par exemple dans ~/.config/git/ignore, puis définir la configuration
git config --global core.excludeFile ~/.config/git/ignore

Quelque soit la méthode choisie, on met dans ce fichier, ligne par ligne, les fichiers à ignorer :

projet.cbp
.DS_Store
config.xml
Mon_mot_de_passe_a_ne_pas_diffuser_sur_GitLab.txt
*.o
*.exe

Les deux dernières lignes permettent d’ignorer tous les fichiers d’extension .o ou .exe.