Git

../_images/git.png

Note

Nous allons nous intéresser à l’aspect gestion de versions de Git: Comment enregistrer l’historique des modifications apportées à un projet.

Le problème

Le code est plus souvent lu qu’écrit

—Guido van Rossum

La démarche naturelle quand on commence à travailler longuement sur un projet pour lequel on veut éviter d’effacer et/ou de perdre son code à cause d’une erreur humaine est de dupliquer les fichiers et de créer des versions multiples d’un même fichier qui sont des instantanés à un moment donnée:

- rapport.doc
- rapport_v1.1.doc
- rapport_v1.doc
- rapport_v2.doc
- rapport_v2_beta.doc
- rapport_vfinale.doc

Il s’agit d’une manière très artisanale de procéder et qui est susceptible d’être à la merci d’une erreur humaine.

La version la plus à jour est-elle rapport.doc ou rapport_vfinale.doc ? Et si on avait un fichier rapport_vfinale2.doc ?

La gestion des versions est un travail fastidieux et méthodique, spoiler, les humains ne sont pas doués pour les travaux fastidieux et méthodique.

Laissons cela à la machine et concentrons-nous sur la partie du travail où nous sommes meilleurs que la machine.

Version Control System

Initialement il s’agit d’une famille de logiciels dédiés à la gestion de code source pour les projets logiciels et également à la documentation, site web, etc.

Leur objectif est de faciliter le travail collaboratif en simplifiant la facilité d’échange des fichiers et de leurs versions, la traçabilité des modifications et la gestion des conflits.

Il existe deux grandes familles de VCS:

  • Systèmes centralisés:
    • CVS (Concurrent Versioning System) vieillissant

    • SVN (Subversion) populaire il y a quelques temps déjà

  • Systèmes décentralisés:
    • Git

    • Mercurial (Hg)

    • Bazaar (bzr)

Ces derniers présentent de nombreux avantages:

  • Une sauvegarde (modulo la synchronisation avec un serveur distant)

  • La conservation de l’historique (nominatif) des fichiers « Qui a fait quoi en somme »

  • La possibilité de retour en arrière

  • La fusion des modifications lors du travail collaboratif

  • La visualisation des changements au cours du temps

A propos de Git

Il s’agit d’un logiciel de gestion de versions décentralisé, créé par Linus Torvalds (auteur du noyau Linux), sa popularité s’explique par sa rapidité (mise en oeuvre et exécution), le travail par branches possible.

Celui-ci est assez complexe à utiliser, comme tout les CVS, toutefois quelques commandes suffisent pour une utilisation quotidienne.

Initialement il s’agit d’un logiciel pour Linux, il existe maintenant une version pour Windows.

Attention

L’utitilisation de Git nécessite certaines notions préalables:

  • Fonctionnement d’un filesystem

  • Connaissance basique du terminal Linux

  • Potentiellement, un grand nombre de commandes

Indication

GIT est conçu pour les fichiers de type texte, toutefois il fonctionne aussi bien avec des fichiers binaires (images, bureautique, etc.).

Notion de base

Dépôt - repository

Le répertoire caché .git est nommé dépôt (repository), il contient toutes les données dont Git a besoin pour gérer l’historique, seul les commandes Git peuvent modifier son contenu.

Commit

L’historique d’un projet est une séquence de snapshot contenant l’état de tous les fichiers du projet. Il s’agit des commits qui possèdent: - une date - un auteur - une description textuelle

Note

En pratique GIT ne stocke pas la totalité des fichiers pour chaque commit mais seulement les différences par rapport à l’état suivant. Ceci a pour avantage d’être moins coûteux en stockage.

Copie de travail

Working copy, il s’agit des fichiers présents dans le répertoire géré par Git, leur état peut être différent du dernier commit de l’historique.

Index

Il s’agit d’un espace temporaire contenant les modifications prêtes à être « commitées ». Ces modifications peuvent être une création de fichier, une modification de fichier ou encore une suppression de fichier.

Utilisation de la commande git

Initialisation d’un dépôt git dans le répertoire courant
$ git init

Note

Initialise la gestion de version dans un répertoire (le répertoire courant) en créant le sous-répertoire .git

Obtenir des informations sur le statut du dépôt
$ git status
Obtenir les détails des changements sur les fichiers du dépôt
$ git diff
Ajouter un fichier dans l’index
$ git add <filename>
Retirer un fichier dans l’index
$ git reset <filename>
Créer un commit à partir des modifications ajouter dans l’index, en commentant celui-ci
$ git commit -m "Ceci est un commentaire pertinent"

Attention

Avant toute utilisation de la commande commit, il convient d’ajouter des éléments à l’index via la commande add

Commande git

Visualisation dans l’historique

git commit -m "Initial commit"

../_images/git01.png

git commit -m "Add app.py"

../_images/git02.png

git commit -m "Fix issue on int"

../_images/git03.png
Consulter l’historique
$ git log
Afficher le détail d’un commit particulier
$ git show <commit-id>
Cycle de vie git

Cycle de vie des commandes git en local

Dépôt distant

Un Remote repository est un dépôt GIT tout à fait similaire à un dépôt local mais accessible via une URL.

Indication

Un dépôt local peut être lié à un dépôt distant

L’utilisation d’un remote repository présente l’intérêt

  • d’une sauvegarde à distance du projet,

  • d’un travail sur plusieurs machines

  • d’un projet accessible facilement

  • et d’une collaboration à plusieurs sur un projet

Cycle de vie git remote

Cycle de vie des commandes git via un dépôt distant

Sa mise en oeuvre passe par plusieurs étapes:

  1. Créer un dépôt sur un serveur (gitlab.com, framagit.org, github.com, etc.)

  2. Cloner un dépôt distant

  3. Publier des commits

  4. Récupérer les commits

Cloner un dépôt distant
$ git clone <repository-url>
Publier des commits
$ git push <repository-name> <branch-name>

Note

  • repository-name: par défaut origin

  • branch-name: par défaut master

Désynchronisation

Le push n’est possible que si la branche locale contient tous les commits de la branche distante.

Si la branche distante contient des commits inconnus en local, il faudra au préalable les récupérer.

Récupérer des commits
$ git pull

Important

Cette opération copie dans le dépôt local les commits distants et met à jour la copie de travail.

Pour éviter les problèmes avec Git

Quand réaliser un commit ?
  • Dès qu’il y a une avancée du programme qui fonctionne.

  • Ne pas commiter du code qui ne fonctionne pas.

  • Le concepteur de Git, Linus Torvalds, préconise de commiter le plus souvent possible.

Quand réaliser un push ?
  • Dès que des fonctionnalités deviennent intéressante pour les autres membres de l’équipe.

  • Afin de pas perturber les autres membres de l’équipe, il faut éviter de réaliser des pushs à répétition.

  • Il est possible d’éditer un commit avant qu’il ne soit publié.

  • Une fois qu’un commit est publié : il ne peut plus être modifié.

Ne pas versionner les fichiers inutiles

Tous les fichiers ne doivent pas être soumis au gestionnaire de versions (les fichiers temporaire, le .exe généré par Visual Studio, etc.)

L’utilisation du fichier .gitignore est plus que recommandé notamment par l’utilisation du site gitignore.io pour générer un fichier .gitignore de base.

Les branches

Lors du développement d’un logiciel, il est primordial d’introduire une nouvelle fonctionnalité sans « casser » le projet.

On souhaite basculer instantanément de la version stable à la version en développement.

C’est ce que nous permettent de faire les branches.

Note

La branche par défaut master, l’idéale serait de ne jamais commiter dans master.

Il convient de créer des branches feature_{x} lorsque une fonctionnalité est développée (dans la réalité, il y a autant de branche feature que de fonctionnalité à developper).

Pour les corrections urgentes, l’utilisation des branches hotfix_{x} est recommandée.

Ainsi la branche master ne contient que du code propre. Si un utilisateur fait une erreur, il n’impact que sa propre branche : il ne pollue pas l’ensemble du dépôt.

Création de branche

$ git branch <NOM>

Depuis la branche parente, souvent master

Créer une nouvelle branche et passer dessus pour l’utiliser
$ git checkout -b feature_x
Pour retourner sur master
$ git checkout master

Fusion de branches

Le sprint a été lancé. Une fonctionnalité à été développée, elle est terminée : il faut donc l’intégrer à la branche master.

Avertissement

La fusion se fait sur la branche courante, pensez à changer de branche avant de fusionner !

Se placer dans la branche réceptrice master
$ git checkout master
Merger la branche
$ git merge feature_x

Note

  • Si la branche feature_x n’a pas été modifiée depuis le début du développement de la fonctionnalité alors Git intégre les commits de feature_x à master. Il s’agit du merge fast-forward. La branche master et feature_x sont désormais équivalentes, il convient de supprimer feature_x.

  • Si la branche feature_x avait évolué avant la fin du développement de la fonctionnalité. Le merge fast-forward n’aurait pas été possible !Un nouveau commit aurait été créé par Git pour fusionner les deux branches.

Important

Si on souhaite conserver la branche feature_x distincte de master

$ git merge --no-ff feature_x

Un commit de fusion des deux branches sera créé, l’historique est préservé.

Branches de développement

Pour corriger un bug numéroté 1138, nous devons entreprendre les actions suivantes:

  1. Création d’une branche bugfix_1138 à partir de la branche stable (master)

  2. Bascule sur la branche bugfix_1138

  3. Correction du bug (commit/push)

  4. Fusion de la branche bugfix_1138 avec la master via la commande merge

Commande git

Historique git

$ git commit -m "Initial commit"
$ git commit -m "Add app.py"
../_images/git_merge00.png
$ git branch bugfix_1138
$ git checkout bugfix_1138
../_images/git_merge01.png
$ git commit -m 'Fix issue on int'
../_images/git_merge02.png
$ git checkout master
$ git merge bugfix_1138
../_images/git_merge03.png

Une petite mise en jambe avec Git

Pour obtenir un dépôt Git sur lequel travailler, deux options sont possibles:

  • Création d’un dépôt vide,

  • Copie (clone dans le langage Git) d’un dépôt existant pour travailler sur une copie de travail.

Nous nous intéresserons à la deuxième option qui fait partie de la vie courante d’un développeur.

Git a plusieurs interfaces utilisateur, la plus complète étant l’interface en ligne de commande (CLI) nous nous servirons de celle-ci.

À faire

  • Tout d’abord il convient de créer un compte sur une forge git comme Gitlab.

  • Créer un premier repository HelloWorld via l’interface en ligne de Gitlab

  • Cloner ce repository sur votre ordinateur via la commande clone

  • Quel commande utiliser pour afficher le statut de ce dépôt ?

Attention

Attention à partir de maintenant nous n’utiliserons l’interface web de gitlab uniquement pour constater les changements de données sur notre dépot distant. Lorsqu’il vous est demandé de créer des fichiers, il convient de le faire sur votre machine via un éditeur de code.

Astuce

Un fichier readme (en français, lisezmoi) est un fichier contenant des informations sur les autres fichiers du même répertoire. L’extension .md indique qu’il s’agit d’un fichier formaté en Markdown.

À faire

  • Créer le fichier README.md

  • Ajouter à ce fichier le contenu suivant:

# HelloWorld

## About

A simple repo for learning git

## Author

Insert your name here

Pour intégrée dans l’historique des révisions du dépôt (pour être « committée »), chaque modification doit suivre le workflow suivant:

Note

  1. La modification est d’abord effectuée sur la copie de travail;

  2. Elle est ensuite mémorisée dans une aire temporaire nommée index avec la commande git add;

  3. Enfin ce qui a été placé dans l’index peut être « committé » avec la commande git commit

@startuml
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

   "Working Copy" as WorkingCopy ->  Index: git add
   Index --> WorkingCopy: git reset
   Index -> Repository: git commit
@enduml

Diagramme UML du Workflow d’une modification avec Git

Vérifiez avec git status l’état dans lequel se trouve votre dépôt. Vos modifications (l’ajout du fichier README.md) devraient être présentes seulement dans la copie de travail.

À faire

  • Préparez README.md pour le commit avec git add README.md.

  • Utilisez git status à nouveau pour vérifier que les modifications ont bien été placées dans l’index.

  • Commitez votre modification avec git commit -m "<votre_message_de_commit>". Le message entre double quotes décrira la nature de votre modification (généralement inférieur à 65 caractères).

  • Exécutez à nouveau:code:git status, pour vérifier que vos modifications ont bien été commités.

  • Essayez à présent la commande git log pour afficher la liste des changements effectués dans ce dépôt ; combien y en a-t-il ? Quel est le numéro (un hash cryptographique en format SHA1) du dernier commit effectué ?

Vos modifications sont prises en compte dans votre dépôt local, toutefois via la commande git status vous pouvez remarquer que le commit n’est pas publié sur le dépôt distant.

@startuml
scale 2.5
skinparam backgroundcolor transparent
skinparam defaultFontName Hack

    "Working Copy" as WorkingCopy ->  Index: git add
    Index --> WorkingCopy: git reset
    Index -> Repository: git commit
    Repository -> "Remote repository" as RemoteRepo: git push

@enduml

Diagramme UML du Workflow d’une modification avec Git

À faire

  1. Publiez votre commit avec git push origin master. Que signifie origin et master ?

  2. Exécutez à nouveau git status, pour vérifier que votre commit a bien été publié.

Une histoire de Burger

Dans note repository nous allons créer un répertoire sandwich qui contiendra le fichier burger.txt.

Le fichier burger.txt contient la liste des ingrédients d’un burger, un ingrédient par ligne.

steak
salade
tomate
cornichon
fromage

À faire

  • Comment vérifier que ce fichier n’est pas versionné ?

  • Comment l’ajouter à l’index ?

  • Comment le « committer » ?

  • Comment publier ce commit ?

À faire

Créez quelques autres sandwichs hot_dog.txt, jambon_beurre.txt et modifiez les compositions déjà créés, en committant chaque modification séparément.

Chaque commit doit contenir une et une seule création ou modification de fichier. Effectuez au moins 5 modifications différentes (et donc 5 commits différents).

À chaque étape essayez les commandes suivantes :

  • git diff avant git add pour observer ce que vous allez ajouter à l’index;

  • git diff --cached après git add pour observer ce que vous allez committer.

Regardez maintenant l’historique des modifications avec git log et vérifiez avec git status que vous avez tout commité.

Voyage dans le temps

Vous voulez changer d’avis entre les différents états de la Figure sur l’état des modifications ? Faites une modification d’un ou plusieurs sandwichs, ajoutez-la à l’index avec git add (vérifiez cet ajout avec git status ), mais ne la commitez pas. Exécutez git rest sur le nom de fichier (ou les noms de fichiers) que vous avez préparés pour le commit ; vérifiez avec git status le résultat.

À faire

  • Votre modification a été « retirée » de l’index.
    • Vous pouvez maintenant la jeter à la poubelle avec la commande git checkout sur le ou les noms des fichiers modifiés, qui récupère dans l’historique leurs versions correspondant au tout dernier commit.

    • Essayez cette commande, et vérifiez avec git status qu’il n’y a maintenant plus aucune modification à commiter.

  • Regardez l’historique de votre dépôt avec git log ;
    • choisissez dans la liste un commit (autre que le dernier).

    • Exécutez git checkout COMMITIDCOMMITID est le numéro de commit que vous avez choisi.

    • Vérifiez que l’état de vos sandwichs est maintenant revenu en arrière, au moment du commit choisi.

    • Que dit maintenant git status ?

  • Vous pouvez retourner à la version plus récente de votre dépôt avec git checkout master.

  • Vérifiez que cela est bien le cas. Que dit maintenant git status ?