3. Système de gestion de versions: git🔗

La gestion des versions du code permet de suivre avec précision l'historique des modifications du code source (python ou tout autre langage), de retrouver les changements critiques, de développer des branches expérimentales, de faciliter le travail collaboratif, etc.

Git est un VCS distribué particulièrement performant [1] (et libre). Contrairement aux VCS centralisés, le dépôt local dispose de l'intégralité de l'historique de développement, ce qui permet des opérations locales plus rapides. Il est cependant souvent couplé à un dépôt en ligne faisant office de référence et de solution de sauvegarde, et offrant généralement des solutions d'intégration continue, p.ex.:

  • les célèbres GitHub et GitLab, gratuits pour les projets libres;

  • pour des projets liés à votre travail, je conseille plutôt des dépôts directement gérés par votre institution, p.ex. GitLab-IN2P3.

git mérite un cours en soi, et devrait être utilisé très largement pour l'ensemble de vos projets utilisant des fichiers sources de type texte (p.ex. rédaction d'articles ou cours en LaTeX, fichiers de configuration, projets informatiques, etc.).

Quelques liens d'introduction:

Cheet sheats (aides mémoire):

3.1. Généralités🔗

  • git agit finalement comme un mini-système de fichiers, où toutes les versions de tous les fichiers sont accessibles, mais regroupées dans des « instantannés » (snapshots), correspondant à un état figé du système à un instant donné.

  • L'intégrité du système est constamment vérifiée une « empreinte » (somme de contrôle) SHA-1. Ainsi, chaque opération peut être identifiée par une chaine (hash) du type 030edc340e16421cf1975335cd085e61f7be57fb (le début de la chaîne, p.ex. 030edc3, suffit à l'identification s'il n'y a pas d'ambiguïté entre différentes opérations).

  • Il existe de nombreuses interfaces à git (p.ex. interfaces graphiques, ou interfaces web), nous utiliserons d'abord la ligne de commande. Toutes les commandes sont de la forme git commande, p.ex.:

    $ git help status
    $ git status --help   # manuel d'utilisation
    $ git status -h       # rappel concis des options
    

    git est assez bavard, et propose souvent que faire en cas d'erreur ou par la suite. Lisez les messages git!

  • La configuration générale de l'outil se fait à l'aide de la commande git config, p.ex.:

    $ git config --global user.name "John Doe"
    $ git config --global user.email johndoe@example.com
    $ git config --global core.editor emacs
    $ git config --list
    
  • Terminologie courante:

    • répo: répertoire sous contrôle git. La version locale (un répertoire spécifique dans votre arborescence) peut être connectée à une version distante.

    • commit: c'est l'action de figer l'état des fichiers dans un instantanné du répo; chaque commit est identifié par son hash, p.ex. 030edc3.

3.2. Initier un répo🔗

  • git init permet de placer un répertoire local sous contrôle git.

  • git clone permet de cloner un répo (généralement distant), i.e. en avoir une copie intégrale:

    $ git clone https://gitlab.example.com/namespace/myproject.git
    
  • Inversement, pour héberger un répo local sur un dépôt en ligne:

    $ git push --set-upstream https://gitlab.example.com/namespace/myproject.git master
    $ git remote add origin https://gitlab.example.com/namespace/myproject.git
    

Serveur gitlab.in2p3.fr

Les étudiants de l'Université Claude Bernard Lyon 1 peuvent utiliser le serveur Gitlab-IN2P3 via l'identification EduGain (pxxx).

Une fois connecté, vous devez initialiser le mot de passe que vous utiliserez pour intéragir dictement avec le serveur. Votre login est votre adresse email de l'Université.

Vous pouvez alors créer un nouveau projet (pratiquement vide) directement depuis l'interface web, et le cloner sur votre machine locale. À l'initialisation, le projet peut avoir différents niveaux de visibilité:

  • privé: accessible uniquement aux personnes explicitement autorisées,

  • semi-privé: accessible à toutes les personnes ayant un compte sur le serveur,

  • public: accessible à tout le monde.

Pour que vos collaborateurs puisse contribuer au développement du code, vous devez les inviter comme membre de votre projet (sur la colonne de gauche) et leur donner des permissions (p.ex. developper pour avoir le droit de déposer du code sur votre répo, dont vous êtes par défaut maintainer).

Avertissement

Si vous ne pouvez pas cloner le répo gitlab.in2p3.fr avec une erreur du type server certificate verification failed, voici une solution:

$ openssl s_client -showcerts -servername gitlab.in2p3.fr -connect gitlab.in2p3.fr:443 </dev/null 2>/dev/null | \
sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' > ~/gitlab-in2p3-fr.pem

puis:

$ cat ~/gitlab-in2p3-fr.pem | sudo tee -a /etc/ssl/certs/ca-certificates.crt

si vous êtes root, ou sinon:

$ git config --global http."https://gitlab.in2p3.fr/".sslCAInfo ~/gitlab-in2p3-fr.pem

3.3. Commandes de base🔗

  • Un fichier sous git peut être dans 3 états:

    • modified: le fichier est modifié localement,

    • staged: le fichier est marqué comme prêt à être « figé » lors du prochain commit,

    • committed: le fichier a été figé dans un snapshot et inclu dans l'historique.

    https://git-scm.com/book/en/v2/images/areas.png

    Figure 3.6 Les 3 états d'un fichier sous Git: modified, staged et committed.🔗

  • git add (synonyme de git stage) permet de placer un fichier modifié (ou un répertoire) sur la staging area, prêt pour le prochain commit.

  • git commit créé un nouveau commit à partir des fichiers de la staging area (ou directement du statut modified avec l'option -a|--all).

    Note

    Le message de commit est obligatoire, faites en sorte qu'il soit pertinent.

    Note

    Commit frequently!

  • Inversement, git checkout permet de mettre à jour le répertoire à partir d'un commit, une branche, etc.

  • git status liste l'état des fichiers du répertoire.

    https://git-scm.com/book/en/v2/images/lifecycle.png

    Figure 3.7 Le cycle de vie des états des fichiers.🔗

$ mkdir demo
$ cd demo/
$ git init              # initialisation du repo git
Initialized empty Git repository in [...]/demo/.git/
$ ls -a
./ 	../	.git/
$ echo "première ligne" > fichier1.txt  # création du fichier1.txt
$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	fichier1.txt

nothing added to commit but untracked files present (use "git add" to track)
$ git stage fichier1.txt      # mise en scène du fichier
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   fichier1.txt
$ git commit                  # snapshot!
/édition du message de commit, obligatoire!/
[master (root-commit) ac68954] Mon premier commit!
 1 file changed, 1 insertion(+)
 create mode 100644 fichier1.txt
$ git status
On branch master
nothing to commit, working tree clean
$ git log
commit ac68954fa1e8a0d3ce90404f73800fad0569c367 (HEAD -> master)
Author: Yannick Copin <y.copin@ipnl.in2p3.fr>
Date:   Wed Sep 21 22:37:57 2022 +0200

    Mon premier commit!
$ echo "deuxième ligne" >> fichier1.txt     # modification de fichier1.txt
$ echo "et une autre ligne" > fichier2.txt  # création de fichier2.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   fichier1.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	fichier2.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ git add fichier2.txt         # ajout du fichier2.txt
$ git commit --all --message "Mon deuxième commit."  # raccourci stage + commit
[master 1b45b71] Mon deuxième commit.
 1 file changed, 1 insertion(+)
 create mode 100644 fichier2.txt
$ git log --graph --oneline --decorate
* 1b45b71 (HEAD -> master) Mon deuxième commit.
* ac68954 Mon premier commit!
$ ls
fichier1.txt	fichier2.txt
$ git checkout ac68954         # Restauration du snapshot antérieur
Note: switching to 'ac68954'.
[...]
HEAD is now at ac68954 Mon premier commit!
$ ls
fichier1.txt                   # à l'époque, seul fichier1.txt existait
$ git checkout master          # Retour au snapshot le plus récent
$ ls
fichier1.txt	fichier2.txt
  • git diff permet de comparer différentes versions.

  • git rm permet de retirer un fichier de l'historique.

  • git mv permet de déplacer ou de renommer un fichier du répertoire sans perdre son histoire.

Attention

a priori, n'incluez dans l'historique git que des fichiers source (texte), et évitez les fichiers binaires (généralement volumineux et se prêtant mal à la comparaison automatique).

Pour ignorer systématiquement certains fichiers (p.ex. les fichiers binaires *.pdf), il est possible de les spécifier dans le fichier .gitignore à la racine du projet [2]:

$ cat .gitignore
*~       # Tous les fichiers de backup *~
*.pdf    # Tous les fichiers *.pdf
_build/  # Tous les répertoires _build/

3.4. Annuler des opérations🔗

  • git commit --amend permet de compléter (et de remplacer) le dernier commit par de nouveaux changements:

    $ git commit -m 'commit initial'
    ZUT! J'ai oublié d'inclure 'fichier_oublié'...
    $ git add fichier_oublié
    $ git commit --amend  # le 1er commit incomplet n'a jamais existé!
    
  • git reset HEAD fichier permet de retirer un fichier de la staging area (sans pour autant le restaurer localement).

  • git checkout -- fichier permet de restaurer un fichier à partir de son dernier instantanné (et donc d'écraser les modifications locales).

3.5. Branches et fusions🔗

Attention

Do not rebase commits that exist outside your repository.

3.6. Synchronisation des dépôts🔗

  • git remote permet de gérer la relation aux dépôts distants:

    $ git remote -v
    origin      git@gitlab.in2p3.fr:user/projet.git (fetch)
    origin      git@gitlab.in2p3.fr:user/projet.git (push)
    $ git remote add <name> <url>
    $ git remote remove <name>
    

    Généralement (p.ex. lors d'un clonage), le dépôt distant est nommé origin.

  • git pull (raccourci pour git fetch et git merge) permet de mettre à jour le répo local à partir du dépôt distant:

    $ git pull origin master  # màj la branche master locale à partir d'origin
    
  • git push permet au contraire de déposer les modifications locales sur répo distant:

    $ git push origin master  # màj de la branche master sur origin
    

Note

si vous travaillez sur un dépôt distant partagé avec d'autres développeurs, n'oubliez pas de mettre à jour votre répo local (git pull) très régulièrement, pour ne pas le laisser diverger. Inversement, git push vos modifications régulièrement sur le dépôt distant.

Voir également:

Notes de bas de page