Gestion de versions du code : Git

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 à fait quoi

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-shell -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

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 blobs 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

Prenons le dépôt https://github.com/Taeradan/hahp :

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

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

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

  4. 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 différents !

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>

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

À voir en live

Contact