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 posteAlice et Bob effectuent des modifications chacun·e de leur côtéAlice écrit sur le dépôtBob 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++.
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".
Ensuite, vous améliorez l’implémentation.
⇒ 2ème commit : "Nouvelle action de la case Chance".
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 modificationsGit stocke des instantanés de chaque version et optimise quand nécessaire → plus efficace
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) :
Configurer son nom et son adresse mail avec git config (cf. la section 1.6 du livre Pro Git).
Ensuite :
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.
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 :
Utiliser git log et chercher le commit dont le SHA-1 commence par d1ddcec8.
(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 :
Modifier le fichier .git/info/exclude dans le dossier .git à la racine du dépôt.
Créer un fichier .gitignore dans un dossier contenant des fichiers à ignorer.
Créer un fichier où vous voulez servant de « .gitignore » global, par exemple dans ~/.config/git/ignore, puis définir la configuration