Gestion de versions du code

Objectifs du cours

  • Comprendre l’intérêt du versionnement dans un projet de développement

  • Avoir une vision d’ensemble des différents systèmes de versionnement

  • S’approprier un outil de gestion de versions

La vie sans gestion de version

Système CPOLD

Le projet démarre

super_projet/
└── rapport.md

Grosse modification, gardons l’ancienne au cas où…​

super_projet/
├── rapport.md
└── rapport-v2.md

Chouette, un gentil relecteur :)

super_projet/
├── rapport.md
├── rapport-v2-relecture_bob.md
└── rapport-v2.md

C’est fini, on livre

super_projet/
├── rapport.md
├── rapport-v2-relecture_bob.md
├── rapport-v2.md
└── rapport-final.md

Oups, coquille !

super_projet/
├── rapport.md
├── rapport-v2-relecture_bob.md
├── rapport-v2.md
├── rapport-final-correction.md
└── rapport-final.md

On a quelques soucis

  • Convention de nommage

    • Au bon vouloir de chacun

    • Ordre réel des versions pas évident à deviner

  • 5 fichiers pour un rapport

  • Synchronisation compliquée

    • Échanges par mail, stockage externe

    • Même fichier → contenu différent pour chacun

Les outils de gestion de version

Pourquoi faire ?

  • Déléguer à un système fiable l’historique de nos fichiers

  • Revenir à une version antérieure

  • Traçabilité : qui a fait quoi et quand

Systèmes centralisés

  • Un serveur contient tout l’historique des fichiers du projet

  • Chaque utilisateur possède une vue des fichiers

  • Un commit consiste à envoyer les modifications au serveur

centralised vcs

Exemples

CVS

1986, peut encore être rencontré en entreprise, utilisé par NetBSD et OpenBSD

Subversion

2000, successeur de CVS, encore massivement utilisé

Systèmes distribués

  • Chaque utilisateur possède son historique du projet

  • Un commit consiste à enregistrer les modifications dans la base de données locale

  • Il est toujours possible d’avoir un serveur central pour référence

decentralised vcs

Exemples

Darcs

2003, écrit en Haskell et pensé pour les projets Haskell

Bazaar

2005, supporté par Canonical (Ubuntu)

Git

2005, écrit par Linus Torvalds pour le développement du noyau Linux

Git !

Généralités

  • Gestionnaire de versions distribué

  • Créé par Linus Torvalds en 2005 pour la gestion des sources du noyau Linux

  • Le nom "git" est une référence directe à l’auteur…​

    I’m an egotistical bastard, and I name all my projects after myself. First Linux, now git.

    — Linus Torvalds

Fonctionnement

  • Git ne retient pas une suite de différences, mais des snapshots des fichiers

  • La majorité des opérations sont locales

  • Git ne fait pratiquement qu’ajouter des données à sa base

  • Chaque utilisateur peut disposer de tous les commits

  • ⇒ Serveur pas indispensable

Démarrer avec Git

Installation : Linux

Git est dans les paquets de toutes les distributions, par exemple :

  • Debian et dérivés

    $ sudo apt update
    $ sudo apt install git tig git-cola
  • NixOS

    $ nix-console -p gitFull tig git-cola

Installation: Windows

WSL

Linux dans Windows 10, pour les gens sérieux mais coincés sur Windows

Git pour Windows

Simple et minimaliste

Cygwin

Complexe, mais puissant, une collection d’outils Linux compilés pour Windows

Préparation de Git

$ git config --global user.name "Your Name"
$ git config --global user.email "foo@bar.org"

Créer un dépôt local

$ mkdir super_projet
$ cd super_projet
$ git init .

Ajouter un premier fichier

$ echo "salut" > rapport.md
$ git add rapport.md
$ git commit -m "commit initial"

États du fichier

addcommit

Référence utile

Les entrailles de Git

Git est un graphe

git graph
  • commit : 0 à n parent(s) + auteur + committer + message + état des fichiers

  • "master", "relecture" : branche, référence à un commit

  • "HEAD" : référence à la branche ou au commit sur lequel est basé l’espace de travail

Exploration du graphe

Que contient la référence master de super_projet ?

$ cat .git/refs/heads/master
8b3e8234ccad7d9213bb28d1e70351ebeff965c8

Git peut afficher le contenu du blob correspondant au hash, ici le commit référencé par la branche master

$ git cat-file -p 8b3e8234ccad7d9213bb28d1e70351ebeff965c8
tree 1ee6881b1653531a8fc24057be0c61d82b8325de
parent b4c8004bffba2d54753f906f5aa146a6d1ff4bef
author Yves Dubromelle <yves+git@dubronetwork.fr> 1587331162 +0200
committer Yves Dubromelle <yves+git@dubronetwork.fr> 1587331162 +0200

greeetings

Hash du commit parent ?

$ git cat-file -p b4c8004bffba2d54753f906f5aa146a6d1ff4bef
tree 19babb3bd3373b4b98e6d0a3b03aef4606dac4b6
author Yves Dubromelle <yves+git@dubronetwork.fr> 1587311802 +0200
committer Yves Dubromelle <yves+git@dubronetwork.fr> 1587311802 +0200

commit initial

Qu’y a-t-il dans tree ?

$ git cat-file -p 1ee6881b1653531a8fc24057be0c61d82b8325de
100644 blob 28d0af969b32e69a389087d7a267a2ecc05f1350	rapport.md

Contenu de rapport.md ?

$ git cat-file -p 28d0af969b32e69a389087d7a267a2ecc05f1350
coucou

Tous ces objets référencés par leur hash sha1 sont présents dans .git/objects !

$ tree .git/objects/
.git/objects/
├── 19
│   └── babb3bd3373b4b98e6d0a3b03aef4606dac4b6
├── 1e
│   └── e6881b1653531a8fc24057be0c61d82b8325de
├── 28
│   └── d0af969b32e69a389087d7a267a2ecc05f1350
├── 8b
│   └── 3e8234ccad7d9213bb28d1e70351ebeff965c8
├── b4
│   └── c8004bffba2d54753f906f5aa146a6d1ff4bef
├── e6
│   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
├── info
└── pack

8 directories, 6 files
git graph complete

Exercice

  1. Clonez le dépôt : git clone https://github.com/Taeradan/hahp

  2. Déplacez-vous dans le dossier nouvellement créé

  3. Déplacez-vous dans la branche webapi : git checkout webapi

  4. Quel est le contenu de l’objet correspondant au dernier commit de la branche webapi ?

  5. Quels sont les parents du commit 94fe1e8bc88b76635a9e4a0d9346c1eebccd6b4f ?

Travailler avec les branches

On veut relire notre rapport, création d’une branche relecture :

$ git checkout -b relecture

On corrige notre rapport :

$ echo "Salut tout le monde !" > rapport.md
$ git add rapport.md
$ git commit -m "plus d'enthousiasme"

Entre temps, le travail continue sur master :

$ git checkout master
$ echo "Projet ayant pour but de tester Git" > readme.md
$ git add readme.md
$ git commit -m "ajout readme"

L’état du projet :

$ git log --graph --decorate --oneline --all
* 0428e9b (HEAD -> master) ajout readme
| * 188be3e (relecture) plus d'enthousiasme
|/
* 8b3e823 greeetings
* b4c8004 commit initial

Aparté : les alias

La commande précédente est longue à taper, mais néanmoins très utile. On va utiliser un alias git pour se simplifier la vie :

$ git config --global alias.lola "log --graph --decorate --oneline --all"

On a plus besoin que de taper git lola pour obtenir le même résultat !

Réintégrer le travail de relecture

Il y a deux façons principales de réintégrer le travail de notre branche relecture dans master :

  • Un merge d’une branche dans l’autre

  • Un rebase d’une branche par-dessus l’autre

Merge

On repart du graphe précédent :

merge before

Faire un merge de relecture dans master, c’est deux étapes :

1 : Créer un commit ayant pour parents les derniers commits de chaque branche, donc 6 et 7

merge 1

2 : Déplacer la référence de la branche dans laquelle on merge, ici master, sur le nouveau commit

merge 2

Rebase

Toujours le même graphe de départ :

rebase before

Faire un rebase de relecture par-dessus master, c’est deux étapes :

1 : Trouver le commit à partir duquel les branches ont divergé, ici 5

rebase 1

2 : Rejouer les commits entre le 5 et celui pointé par relecture, ici 6, par-dessus de master

rebase 2

À noter : les commits ayant changé de parents, ils n’ont plus les mêmes hash, ce sont des commits distincts !

Exercice 1

Dans super_projet, on a modifié readme.md dans master et rapport.md dans relecture.

  • On merge relecture dans master

Exercice 2

  1. Dans relecture, sur rapport.md, modifiez "Salut" en "Hello" puis commitez

  2. Dans master, sur rapport.md, modifiez "Salut" en "Coucou" puis commitez

  3. Lancer un merge de relecture vers master

  4. Qu’est-ce qui se passe ?

  5. Quel est le contenu de rapport.md avant de régler le problème ?

Corrections à postériori

Dernier commit

  1. Effectuer les corrections nécessaires dans l’espace de travail

  2. git commit --amend

  3. Modifier le message si nécessaire

Commit quelconque

  1. Relever le hash du commit précédent ou une branche

  2. git rebase --interactive <hash/branch>

  3. Se laisser guider par les commentaires

Opération git

  1. git reflog

  2. Relever le hash de l’opération précédente

  3. git reset --hard <hash>

Collaborer sur du code

  • Projet privé, limité à un groupe restreint

    • Possibilité de travailler directement entre les personnes (SSH)

    • Souvent, utilisation d’un serveur commun par commodité

  • Projet public, ou ne nous appartenant pas

    1. Faire un "Fork"

    2. Modifier le projet

    3. Demander un "merge"

Serveurs Git

  • N’importe quelle machine Linux + SSH

  • Gitea (auto-hébergement, logiciel libre)

  • Gitlab (instance centrale + auto-hébergement, logiciel libre + fonctionnalités payantes, Gitlab Inc.)

  • Github (instance unique, logiciel propriétaire, Microsoft)

Exercice : Pousser du code existant sur Gitlab

  1. Créer un compte sur le serveur Git d’exemple

  2. Configurer une clé SSH par votre profil

  3. Créer un projet dans votre espace Gitlab

  4. Transformer un projet existant en dépôt git local

  5. Exécuter les instructions données à la création pour pousser le code existant dans le projet Gitlab

Contributions externes

Fork

Fork = Embranchement

Action de copier le dépôt d’un projet pour le lire ou le modifier sans perturber le travail du propriétaire.

Merge request

Merge = Fusionner

Demande polie au propriétaire d’un projet d’y inclure nos modifications.

Exercice : Fork et MR

  1. Faire un fork "circulaire" du projet cours-cesi https://gitlab.dubronetwork.fr/Taeradan/cours-git

  2. Enora → Yves

  3. Safiatou → Enora

  4. Clarence → Safiatou

  5. Omar → Clarence

  6. Anaïs → Omar

  7. Nathan → Anaïs

  8. Corentin → Nathan

  9. Samuel → Corentin

  10. Ruben → Samuel

  11. Frédéric → Ruben

  12. Yves → Frédéric

Workflows

Signature de commit

Intérêt de la signature

  • Être certain de la provenance d’un commit

  • Clé cryptographique forte avec GPG

Procédure

  1. Créer une clé GPG

  2. Configurer Git pour utiliser la signature (ou demander la signature pour un commit précis)

  3. Envoyer sa clé GPG sur le serveur Git

Intégration et livraison continues

But de la CI/CD

  • Exécuter un certain nombre d’opérations après un git push

    • Exécuter des tests

    • Vérifier que le projet compile

    • Vérifier des mauvaises pratiques

    • Livrer le résultat

Moyens pour la CI/CD

  • Intégré à Gitlab

  • serveur Git SSH basique

    • Scripts avec inotify sur un

    • Hooks Git

  • Systèmes de CI/CD indépendants

    • Drone

    • Jenkins

Exercice : CI/CD avec Gitlab

  1. Placez-vous dans le projet du TP de C importé précédemment

  2. Activer le CI/CD dans le projet par le menu "Settings" → "General" → "…​project features…​"

  3. Passer par l’éditeur de pipeline dans l’IHM pour créer un squelette de fichier .gitlab-ci.yml

  4. Écrivez un job de CI permettant de compiler le projet

  5. Définissez un artefact contenant le résultat de compilation pour pouvoir le télécharger par l’IHM

Quelques commandes utiles

  • Récupérer un commit depuis une autre branche : git cherry-pick <hash>

    • Le commit désigné sera joué par-dessus la branche courante, tout en restant à son emplacement d’origine

    • En cas de rebase ultérieur de la branche d’origine, le commit en double sera ignoré

  • Remiser des modifications : git stash […​]

  • Référencer un commit particulier : git tag

  • Ignorer des fichiers : .gitignore

Pour aller plus loin

Contact