Git TP

TP de découverte de Git
Author

Ludovic Deneuville, Rémi Pépin

À faire à la maison

Note

Les consignes de ce TP sont données pour une utilisation de GitHub. Il est cependant tout à fait possible de réaliser ce TP avec GitLab dont l’interface est très ressemblante.

⚠️ Il y a tout de même quelques différences :

  • GitHub Repository = GitLab Projet
  • Pour déclarer la clé SSH dans GitLab : cliquez sur votre avatar > Préférences > SSH keys

Introduction

Git est un formidable outil de versionnage de fichiers qui va vous permettre de conserver efficacement l’historique de votre code aussi bien si vous travaillez seul mais également quand vous travaillez à plusieurs. Cet historique est conservé dans ce que l’on appelle un dépôt git.

Sa conception décentralisée fait qu’il est quasi impossible de perdre une donnée définitivement avec git. En particulier, la perte totale d’un ordinateur hébergeant votre code ne représentera qu’une perte minime de code si vous utilisez git correctement.

En outre, de nombreux outils de CI/CD (Continuous Intégration / Continuous Deployment) qui rendent possible l’automatisation de l’intégration et du déploiement de votre code s’appuient sur un dépôt git.

Concepts Clés

Cependant, comme tout logiciel, git demande un peu d’apprentissage, et de la pratique. Pour commencer, nous aurons besoin de certains concepts essentiels pour une utilisation efficace. Par sa conception décentralisée, un dépôt git peut exister en plusieurs endroits en même temps : sur notre machine, sur chaque machine des membre du projet, ou sur un serveur hébergeant le projet.

Nous parlerons de la copie distante (remote en anglais) du dépôt dans le cas du serveur distant. En général, une seule copie distante est utilisée, mais par sa nature décentralisée, plusieurs copies distantes sont possibles (possibilité non incluse dans ce TP).

La copie sur notre machine est la copie locale (local en anglais). Il s’agit de toutes les informations du dépôt concernant l’historique de ses fichiers, et les métadonnées du dépôt. Une distinction subtile est alors faite entre la copie locale et la copie de travail (working copy en anglais) : Nos modifications sur les fichiers du dépôt n’intégrerons la copie locale qu’à notre demande explicite. Cette intégration sera alors effectuée par l’ajout d’une entrée à l’historique du dépôt.

Programme

Pour utiliser git, donc pour gérer un dépôt, nous avons accès à de nombreuses commandes. Pour ce TP de découverte vous allez utiliser seulement les commandes suivantes :

Commande Description
git clone <adr> Créer un dépôt local sur son poste
git status Voir où l’on en est
git add <file> Ajouter pour le prochain commit
git commit -m "<msg>" Créer un point de sauvegarde
git pull dépôt local ➡️ dépôt distant
git push dépôt local ⬅️ dépôt distant

1 Git Bash et Unix

Avant de nous lancer avec git, prenons quelques minutes pour découvrir le terminal Git Bash et les principales commandes Unix.

Ces commandes ne sont pas indispensables pour utiliser git, mais il est utile et préférable de les connaitre.

    • cela ouvre un terminal

Pour vous aider, voici la liste des commandes.

1.1 Création d’une arborescence

  • pwd
  • cd /p
  • ll
  • mkdir Cours1A
  • cd Cours1A
  • mkdir git-initiation
  • cd git-initiation

Il était également possible de tout faire d’une seule traite :

  • mkdir -p /p/Cours1A/git-initiation && cd $_
  • l’option -p de mkdir crée tous les dossiers et sous-dossiers s’ils n’existent pas déjà
  • $_ : correspond au dernier argument de la commande précédente

1.2 Les fichiers

    • ps liste tous les processus en cours
    • ps > a.txt
    • cat b.txt | grep ps
Raccourcis très utiles
  • Les flèches ⬆️ et ⬇️ pour naviguer dans l’historique des dernières commandes et éviter de retaper 10 fois la même commande
  • TAB pour l’autocomplétion
    • Tapez cat b, puis appuyez sur la touche TAB
    • Il n’y a qu’un seul fichier commençant par b donc l’autocomplétion s’effectue
    • La touche TAB se situe à gauche du clavier dessous ² et dessus 🔒

Pour aller plus loin, vous pouvez consulter le chapitre Linux 101 du cours de Mise en production de l’ENSAE.

2 Paramétrage de Git

2.1 Configuration

  • setup-git.sh
    git config --global user.name "Prenom Nom"
    git config --global user.email prenom.nom@eleve.ensai.fr
    git config --global credential.helper store
    git config --global core.mergeoptions --no-edit
    git config --global core.editor "code -w"

2.2 Création d’une clé SSH

Pour permettre de faire dialoguer notre dépôt local avec le dépôt distant de GitHub, vous utiliserez le protocole SSH. Pour cela, vous allez :

  • générer une paire de clés (publique/privée) sur notre machine
  • déclarer votre clé publique à GitHub
Warning

Ce paramètrage SSH est réalisé au niveau de la machine.

Si par exemple, vous souhaitez utiliser Git sur votre ordinateur personnel, il faudra faire également ce paramétrage.

Dans Git Bash :

    • mkdir -p ~/.ssh
    • ssh-keygen -t rsa -b 4096 -N '' -q -f ~/.ssh/id_rsa
    • cat ~/.ssh/id_rsa.pub | clip
    • c’est comme si vous copiez le contenu du fichier id_rsa.pub

Déclarez votre clé publique à GitHub, pour pouvoir ensuite faire communiquer dépôts locaux avec les dépôts distants :

    • Title : VM ENSAI
    • Key : Collez votre clé publique
    • Add SSH key

SSH (Secure Shell) est un protocole permettant de se connecter à un autre ordinateur sur un réseau de manière sécurisée.

SSH chiffre toutes les informations échangées afin de protéger les données.

SSH utilise un mécanisme de clés cryptographiques pour authentifier les ordinateurs et les utilisateurs, garantissant que la connexion est effectuée avec le bon serveur et sans intervention malveillante :

  • Clé privée : C’est comme la clé de votre maison. Vous la gardez en sécurité avec vous et ne la partagez avec personne. Cette clé reste sur votre ordinateur et sert à prouver votre identité.
  • Clé publique : Elle serait comme votre adresse postale. Vous pouvez la partager avec d’autres. Dans SSH, vous placez votre clé publique sur les serveurs ou les ordinateurs auxquels vous souhaitez vous connecter.

Ces deux clés sont liées. Un message chiffré par la clé publique n’est déchiffrable que par celui qui posséde la clé privée. Lorsque vous chiffrez un message avec votre clé privée, vous prouvez à tous votre identité car chacun peut déchiffrer ce message avec la clé publique.

2.3 Le terminal Git Bash

  • Créez un dossier depot1 et entrez dans ce dossier
  • Afficher le chemin du répertoire courant avec la commande pwd : print working directory

3 Créez un dépôt distant

    • GitHub > Repositories > New
    • Repository name : tp-git
    • Visibility Level : Private
    • Cochez Add a README file
    • Add .gitignore : Python
    • Choose a license : MIT Licence
    • Cliquez sur Create Repository

4 Créez un dépôt local

Suite à la création de votre compte GitHub, vous avez créé un repo (i.e. un dépôt distant).

Vous allez maintenant créer une copie locale de ce dépôt en clonant ce projet.

Sur la page GitHub de votre repo,

    • si ce n’est pas le cas : cd /p/Cours1A/git-initiation
    • exemple : git clone git@github.com:ludo2ne/tp-gitzzz.git
    • Le dossier tp-git est bien présent
    • vous pouvez également vérifier sa présence via l’explorateur Windows
      • WIN + E, puis allez à P:\Cours1A\git-initiation\depot1
    • à la fin de votre prompt, vous remarquerez l’apparition de (main)
    • cela signifie que vous êtes dans un dépôt git, sur la branche principale

Vous pouvez fermer Git Bash.

Vous allez par la suite utiliser le Git Bash (exactement le même) qui est intégré dans l’IDE Visual Studio Code.

Tip

Un IDE (Integrated Development Environment) est une application qui offre des outils complets pour le développement logiciel

Les composants principaux d’un IDE :

  • Éditeur de Code
  • Compilateur / Interpréteur
  • Gestionnaire de Projet
  • Terminal Intégré

5 Visual Studio Code - Config

Commençons par déclarer Git Bash comme le terminal par défaut. C’est plus pratique et convivial lorsque l’on utilise git.

6 Votre dépôt local dans VSCode

Dans Visual Studio Code :

    • Allez dans P:/Cours1A/git-initiation/depot1
    • Cliquez une seule fois sur le dossier tp-git
    • Puis sur le bouton Sélectionner un dossier
    • Yes I trust the authors
    • ⚠️ dans votre explorer en haut à gauche, le dossier parent doit être tp-git
      • si ce n’est pas le cas, recommencez l’Open Folder
    • Terminal > New Terminal (CTRL + ù)

7 Premiers pas avec Git

Vous allez commencer par créer de nouveaux dossiers et fichiers sur votre dépôt local.

    • dans VSCode > clic droit dans l’Explorer > New Folder
    • ce dossier contiendra vos programmes
    • dans le dossier src
    • ouvrez ce fichier et collez ce code
    voiture.py
    class Voiture:
        """Classe représentant une voiture.
    
        Attributes
        ----------
        nom : str
            le nom de la voiture.
        couleur : str
            la couleur de la voiture.
        vitesse : int
            la vitesse de la voiture (initalisée à 0).
        """
    
        def __init__(self, nom, couleur):
            """Constructeur"""
            self.nom = nom
            self.couleur = couleur
            self.vitesse = 0
    
        def __str__(self):
          return f"La voiture {self.nom} de couleur {self.couleur} roule à {self.vitesse} km/h."

7.1 Add, Commit, Pull et Push

C’est l’enchainement de commandes qu’il faut connaitre ! (avec une pincée de status si besoin)

Dans le terminal Git Bash intégré dans VScode, exécutez les commandes suivantes :

    • différences entre la version de travail du code et le commit le plus récent
    • le dossier src apparait dans Untracked files
    • cela signifie que Git a repéré ce dossier mais qu’il ne le versionnera pas
    • pour faire reconnaitre ce dossier et le placer la zone de transit
    • ce dossier et ses fichiers seront versionnés dans le prochain commit
    • maintenant le fichier est reconnu par Git
    • permet de faire un commit = un point de sauvegarde pour git = une entrée supplémentaire à l’historique
    • entre les " ", mettez un message court et explicite. Ce message est obligatoire !
    • Your branch is ahead of ‘origin/main’ by 1 commit
    • votre dépôt local est en avance d’un commit par rapport au dépôt distant
    • récupére les derniers commits disponibles sur le dépôt distant (sur GitHub)
    • actuellement il n’y en a pas, mas il faut prendre l’habitude de souvent récupérer les derniers commits
    • pousse vos commits vers le dépôt distant.
    • ⚠️ si vous avez modifié des fichiers qui ne sont pas dans un commit, ces modifications n’apparaitront pas sur le dépôt distant

Allez sur la page GitHub de votre repo et vérifiez que vos fichiers sont bien arrivés.

Retournez sur VSCode.

  • voiture.py
        def accelere(self, increment) -> None:
            """Augmente la vitesse de la voiture.
    
            L'incrément maximal est de 10 km/h.
            La Vitesse maximale est de 130 km/h.
    
            Parameters
            ----------
            increment : int
                la valeur de l'accélération demandée (limité à 10)
            """
            if increment > 10:
                increment = 10
            self.vitesse = min(130, self.vitesse + increment)

Dans le terminal Git Bash

    • le fichier voiture.py a été modifié
    • q pour quitter

7.2 Historique du dépôt

    • en cliquant sur Commit (en dessous du bouton Code)
    • historique d’un fichier en ouvrant ce fichier et en cliquant sur History
    • ce n’est pas aisément lisible
    • heureusement il est possible d’améliorer cette commande
Pour avoir un résultat plus agréable
    • les 2 dépôts sont de nouveau synchronisés

Pour résumer voilà ce que vous avez fait :

sequenceDiagram
    Workspace ->> Staging index: add
    Staging index ->> Local repository: commit
    Local repository ->> Remote repository: push
    Remote repository ->> Workspace: pull

7.3 Exercice d’application

  • fibonacci.py
    def fibonacci(n):
        """Calcule le n ième terme de la suite de Fibonacci
        en utilisant un algorithme récursif.
        """ 
        if n < 2:
            return 1
        else :
            return fibonacci(n - 1) + fibonacci(n - 2)
    
    if __name__ == "__main__":
        for i in range (1, 15):
            print(fibonacci(i))
  • puissance_rec.py
    def puissance_rec(nombre, puissance):
        if not puissance:
            return 1
        elif not puissance % 2:
            return puissance_rec(nombre, int(puissance / 2)) \
            * puissance_rec(nombre, int(puissance / 2))
        else:
            return nombre * puissance_rec(nombre, puissance - 1)

7.4 Le fichier .gitignore

Grâce à ce fichier, vous pouvez dire à Git d’ignorer certains fichiers.

  • joueuses.csv
    id_joueuse,nom,prenom,date_naissance,pays
    1,Sebag,Marie,1986-10-15,France
    2,Polgar,Judit,1976-07-23,Hongrie
    3,Hou,Yifan,1994-02-27,Chine
    4,Kosteniuk,Alexandra,1984-04-23,Suisse
    5,Ju,Wenjun,1991-01-31,Chine
    • le dossier data apparait dans les Untracked files
    • le dossier data n’apparait plus car maintenant Git ignore ce dossier et son contenu
    • il est bien présent dans votre dépôt local mais il n’arrivera donc jamais sur le dépôt distant
Note

Certains fichiers ont vocation à rester seulement sur votre dépôt local, par exemple :

  • fichiers de données
  • fichiers contenant des mots de passe
  • fichiers de logs

8 Simulation de travail en groupe

Pour les questions suivantes, vous allez avoir besoin de 2 dépôts locaux et d’un dépôt distant. Il y a 2 possibilités :

  • soit vous travaillez en bînome (vous avez chacun votre dépôt local)
  • soit vous faîtes seul (mais vous gérez 2 dépôts locaux différents)
Si vous travaillez à 2

Un des 2 membres va créer un dépôt local à partir du dépôt distant de l’autre.

    • page GitHub du repo > Settings > Collaborators
    • Add people
    • explorateur Windows > aller dans P:/Cours1A/git-initiation/
    • créez un dossier depot2, et entrez dans ce dossier
    • clic droit > Open Git Bash here
    • git clone <adresse_ssh_depot_distant_de_frog>

Dans l’explorateur Windows :

    • git clone xxx comme fait précédemment
  • Vous avez donc 2 dépôts locaux distincts
    • P:/Cours1A/git-initiation/tp-git 🐸
    • P:/Cours1A/git-initiation/depot2/tp-git 🐱
    • ils sont indépendants mais tous deux reliés au même dépôt distant
    • File > Open Folder > P:/Cours1A/git-initiation/depot2/tp-git
  • moto.py
    class Moto:
        def __init__(self, nom, couleur):
            self.couleur = couleur
            self.nom = nom
            self.vitesse = 0
    
        def accelere(self, increment):
            if increment > 15:
                increment = 15
            self.vitesse = min(150, self.vitesse + increment)
    • il récupére le fichier moto.py dans son dépôt local
    • 🐱 crée un fichier velo.py et le pousse
    • 🐸 le récupère

Nous allons maintenant illustrer le fait que votre dépôt local doit être impérativement à jour pour pousser du code.

Vous allez faire en parallèle (1, 2, 3, partez) :

  • state-diag.md
    ```mermaid
    stateDiagram
        login : Se connecter
        menu_joueur : Menu Joueur
        logon : Créer un compte      
        [*] --> Accueil      
        Accueil --> login
        login --> menu_joueur      
        Accueil --> logon      
        Accueil --> quitter
        quitter --> [*]
    ```
    • git add .
    • git commit -m "<complétez>"
    • git push
Important
  • Pour 🐸 qui a poussé son code en premier ➡️ tout s’est bien passé

  • Pour 🐱, il a du recevoir ce genre de message

    ! [rejected]        main -> dev (fetch first)
    error: failed to push some refs to 'git@github.com:ludo2ne/tp-git.git'
    hint: Updates were rejected because the remote contains work that you do
    hint: not have locally. This is usually caused by another repository pushing
    hint: to the same ref. You may want to first integrate the remote changes
    hint: (e.g., 'git pull ...') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
    • si vous lisez le message, c’est assez clair
    • votre dépôt local est en retard de version par rapport au dépôt distant qui fait foi
    • vous devez donc mettre à jour votre dépôt local avant de pousser
    • il fait donc un pull

8.1 Exercice d’application

8.2 Un premier conflit

Pour le moment vous avez travaillé sur des fichiers différents, donc git arrive à gérer la situation.

Maintenant, que se passe-t-il si vous modifiez tous les deux le même fichier ?

Vous allez tous les 2 travailler sur le fichier voiture.py. Dans la méthode accelere() :

    • si vous avez bien compris, vous savez que le push ne va pas fonctionner
    • Et là c’est le conflit…
    remote: Enumerating objects: 7, done.
    remote: Counting objects: 100% (7/7), done.
    remote: Compressing objects: 100% (3/3), done.
    remote: Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
    Unpacking objects: 100% (4/4), 1015 bytes | 8.00 KiB/s, done.
    From github.com:ludo2ne/tp-git
       c27c708..05380ea  main       -> origin/main
    Auto-merging src/voiture.py
    CONFLICT (content): Merge conflict in src/voiture.py
    Automatic merge failed; fix conflicts and then commit the result.
    • Git vous prévient qu’il n’est pas arrivé à fusionner les commits automatiquement
    • Un conflit est généré quand les mêmes lignes d’un fichier sont modifiés par 2 commits de manière différente

Votre code ressemble à cela désormais à :

    def accelere(self, increment) -> None:
        if increment > 10:
            increment = 10
<<<<<<< HEAD
        self.vitesse = min(110, self.vitesse + increment)
=======
        self.vitesse = min(150, self.vitesse + increment)
>>>>>>> 05380ea70dbd5d4e49371af8da7c0ac1df13a010
  • La première partie (entre <<<<<<< HEAD et =======) correspond au code en que vous aviez dans votre copie de travail
  • La seconde partie est le code provenant du dépôt distant
  • Plusieurs choix s’offrent à vous :
    • Imposer votre version (110)
    • Accepter la version de 🐱 (150)
    • Choisir une toute autre version (par exemple 120)

Résolvons ce conflit

    • et supprime toutes les autres lignes de <<<<<<< à >>>>>>>
    • autre possibilité offerte par VSCode, cliquer sur Accept Current Change
    • vous remarquerez qu’avant le commit, le prompt affichait (main|MERGING)
    • ce qui signifie que vous êtes en train de fusionner du code
Important

Avoir un conflit, ce n’est pas grave !
Avoir un conflit, ce n’est pas grave !
Avoir un conflit, ce n’est pas grave !
Avoir un conflit, ce n’est pas grave !
Avoir un conflit, ce n’est pas grave !

Conflit ≠ Erreur

Cela arrive simplement quand Git rencontre 2 versions et il n’a pas de 🔮, ni de 🎲 pour choisir laquelle est la bonne.

Éviter les conflits
  • Essayez de ne pas coder en même temps sur les mêmes fichiers
  • Faites des pull et push réguliers

Plus le code de votre dépôt local sera “proche” de celui du dépôt distant, moins vous en aurez.

En effet, si vous codez pendant 5 jours sans jamais pousser votre code, vous prenez le risque que d’autres personnes modifient les mêmes fichiers et poussent avant leur code.

8.3 Exercice d’application

Dans le constructeur de la classe Voiture :

  • voiture.py
        def __init__(self, nom, couleur, vitesse_max=130):
          """Constructeur"""
          self.nom = nom
          self.couleur = couleur
          self.vitesse = 0
          self.vitesse_max = vitesse_max
  • voiture.py
        def __init__(self, nom, couleur, carburant):
          """Constructeur"""
          self.nom = nom
          self.couleur = couleur
          self.vitesse = 0
          self.carburant = carburant