.. highlight:: console
.. _git-scm:
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 :abbr:`VCS(Version Control System)`
*distribué* particulièrement performant [#gitW]_ (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:`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:
- le livre « officiel » en accès libre: `Pro-Git book
`_ |en|, `livre Pro-Git
`_ |fr|;
- `Git Immersion `_;
- `Git Magic `_;
- `Cours Git `_ |fr|.
*Cheet sheats* (aides mémoire):
- `GitHub Cheat Sheet
`_ |fr|,
- `Visual Git Cheat Sheet `_.
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
« instantané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) :wfr:`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 :samp:`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:`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`.
Initier un répo
===============
* :git:`git init ` permet de placer un répertoire local sous contrôle
`git`.
* :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 main
$ git remote add origin https://gitlab.example.com/namespace/myproject.git
.. rubric:: Serveur `gitlab.in2p3.fr`
Les étudiants de l'Université Claude Bernard Lyon 1 peuvent utiliser le serveur
`Gitlab-IN2P3 `_ via l'identification `EduGain
`_ (login `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*).
.. Warning:: 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 | \
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 administrateur de la machine (*root*), ou sinon::
$ git config --global http."https://gitlab.in2p3.fr/".sslCAInfo ~/gitlab-in2p3-fr.pem
* `Support gitlab-IN2P3
`_
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.
.. figure:: https://git-scm.com/book/en/v2/images/areas.png
:align: center
:width: 60%
:target: https://git-scm.com/book/fr/v2/Démarrage-rapide-Rudiments-de-Git
Les 3 états d'un fichier sous Git: *modified*, *staged* et *committed*.
* :git:`git add ` (synonyme de :git:`git stage `) permet de
placer un fichier modifié (ou un répertoire) sur la *staging area*, prêt pour
le prochain *commit*.
* :git:`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.
.. hint:: *Commit frequently!*
* Inversement, :git:`git checkout ` permet de mettre à jour le
répertoire à partir d'un *commit*, une branche, etc.
* :git:`git status ` liste l'état des fichiers du répertoire.
.. figure:: https://git-scm.com/book/en/v2/images/lifecycle.png
:align: center
:width: 60%
:target: https://git-scm.com/book/fr/v2/Les-bases-de-Git-Enregistrer-des-modifications-dans-le-dépôt
Le cycle de vie des états des fichiers.
.. literalinclude:: exemple.git
:language: console
* :git:`git diff ` permet de comparer différentes versions.
* :git:`git rm ` permet de retirer un fichier de l'historique.
* :git:`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 [#gitignore]_::
$ cat .gitignore
*~ # Tous les fichiers de backup *~
*.pdf # Tous les fichiers *.pdf
_build/ # Tous les répertoires _build/
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é!
* :samp:`git reset HEAD {fichier}` permet de retirer un *fichier* de la
*staging area* (sans pour autant le restaurer localement).
* :samp:`git checkout -- {fichier}` permet de restaurer un *fichier* à partir
de son dernier instantanné (et donc **d'écraser** les modifications locales).
Branches et fusions
===================
* :git:`git branch `
* :git:`git merge `
* :git:`git rebase `
.. Attention:: *Do not rebase commits that exist outside your repository.*
Synchronisation des dépôts
==========================
* :git:`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
$ git remote remove
Généralement (p.ex. lors d'un clonage), le dépôt distant est nommé `origin`.
* :git:`git pull ` (raccourci pour :git:`git fetch ` et
:git:`git merge `) permet de mettre à jour le répo local à partir du
dépôt distant::
$ git pull origin main # màj la branche main locale à partir d'origin
* :git:`git push ` permet au contraire de déposer les modifications
locales sur répo distant::
$ git push origin main # màj de la branche main 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.
.. rubric:: Voir également:
* `Git Best Practices `_
* `Git Tips and Git Commit Best Practices
`_
.. rubric:: Exercice:
:ref:`Exercices/exo:packaging` (intermède git)
.. rubric:: Notes de bas de page
.. [#gitW] Initialement développé et utilisé pour le `noyau Linux
`_, et maintenant du `code Windows
`_!
.. [#gitignore] Voir une `longue liste d'exemples
`_.
.. |fr| image:: ../_static/france_flag_icon.png
:alt: Fr
.. |en| image:: ../_static/uk_flag_icon.png
:alt: En