TP1 - Gestion de version avec Git
Crédits : Florian Forestier
Présentation de Git
Git est un système de gestion de versions, c'est-à-dire un logiciel qui permet de stocker un ensemble de fichiers tout en gardant une chronologie de toutes les modifications qui ont été effectuées dessus. Cela offre la possibilité de récupérer d'anciennes versions de documents et d'examiner l'historique des modifications. Cela permet également de travailler de manière distribuée, afin que plusieurs personnes puissent collaborer sur un même projet. Pour que la collaboration de plusieurs personnes soit efficace, une fonctionnalité permet de gérer les conflits lors de modifications concurrentes de la même partie d'un fichier.
Il existe deux philosophies de gestionnaire de versions :
- L'une utilisant un système centralisé, comme CVS ou Subversion : dans ce genre de système, il n'y a qu'un seul dépôt des versions. Le serveur va donc centraliser tous les fichiers constituant un projet et contient l'historique des versions. Dans ce système, l'utilisateur va récupérer sur son poste une version des fichiers et va toujours travailler sur celle-ci. Lorsqu'il le souhaitera, il synchronisera son code avec le dépôt central pour que le code du dépôt central soit modifié. Ce système présente plusieurs défauts, le principal étant qu'il est très difficile de travailler sans connexion ;
- L'autre un système décentralisé, comme Git ou Mercurial : dans ces systèmes, chaque utilisateur dispose d'une copie complète du dépôt, avec son historique complet. Il sera certes plus long de cloner un dépôt, mais l'utilisateur pourra alors travailler localement sur son poste, sans connexion à internet. Par ailleurs, en cas de défaillance du serveur central, le code et son historique ne sera pas perdu, puisque tous les utilisateurs disposent d'une copie complète du code source.
Git est le système de gestion de version le plus connu et le plus utilisé de nos jours. Il s'agit d'un CVS décentralisé, créé par Linus Torvalds pour la gestion du noyau Linux. Pour l'anecdote, Git a été conçu en quelques semaines, suite à la décision de BitKeeper de rendre son outil de gestion de version payant.
Démarche
Les fichiers sont stockés dans un dépôt central. Ce dépôt peut être situé sur un serveur distant, ou sur la machine locale. L'accès à un serveur distant peut se faire soit à l'aide d'un protocole spécifique à Git, soit encapsulé dans du SSH ou du HTTP pour être accessible plus facilement.
Afin de travailler, il faut récupérer une copie locale du dépôt ; c'est la copie de travail, sur laquelle les modifications seront apportées. Une fois les modifications effectuées, il faut les commiter (les regrouper pour former un "paquet de changements"), puis les transmettre au dépôt central (push). Ce dernier mettra à jour les versions si tout se passe bien, ou refusera s'il y a un conflit qu'il ne peut pas résoudre seul. Afin de faciliter la gestion des versions, il est très fortement recommandé d'indiquer la liste des modifications effectuées. De la même manière, il est recommandé de fréquemment envoyer ses modifications et récupérer celles des autres, pour rester synchronisé, et éviter les conflits.
Utilisation de Git
Configuration initiale
Avant de commencer, nous allons créer un environnement de travail. Dans un terminal, tapez les commandes suivantes :
mkdir -p ~/TPGenieLog/Git
cd ~/TPGenieLog/Git
Nous allons ensuite configurer Git. Chacun de nos commits dispose en effet du nom de l'auteur des modifications, et son adresse mail. Il faut donc configurer Git pour qu'il les connaisse :
git config --global user.name "Prénom Nom"
git config --global user.email "prenom.nom@etu.uca.fr"
Vous pouvez afficher votre configuration avec la commande
git config --list
.
Commandes de base
Le programme de base (client) ne fonctionne qu'en ligne de commande. Il existe cependant de nombreuses interfaces graphiques (gitGUI, tortoise, etc). La majorité des IDEs modernes embarquent également un visualiseur Git. Ces clients graphiques présentent tous l'inconvénient d'être incomplets et de fonctionner en "magie noire", aussi, il est déconseillé de s'en servir pour autre chose que de la lecture.
Création d'un dépôt
Tout d'abord, nous allons initialiser notre dépôt Git en local. Dans le
dossier ~/TpGenieLog/Git
créé précédemment, nous allons taper la
commande suivante : git init
.
Cette commande va créer un dossier .git
dans votre dossier courant. Ce
répertoire contient toutes les informations nécessaires à Git pour gérer
son dépôt. Les données ne doivent jamais être éditées à la main.
L'arborescence est la suivante :
config
: contient les options de configuration spécifique au projetdescription
: fichier utilisé pour parcourir le dépôt git via interface graphiqueHEAD
: lien symbolique vers la branche courante. En général, redirige vers refs/heads/master.hooks/
: contient les scripts exécutés avant ou après certaines commandes (commit, push, etc)info/
: contient divers fichiers de configurationobjects/
: contient tout le contenu de la base de code versionnérefs/
: contient toutes les références du dépôt (stashes, tags, branches distantes et locales, etc).
Voir le statut du dépôt local
À tout moment, vous pouvez connaître l'état du dépôt en utilisant la
commande git status
. Cette commande va indiquer l'ensemble des
modifications depuis votre dernier commit. Actuellement, elle devrait
vous indiquer qu'aucun changement n'a eu lieu. Mais cela va vite
changer !
Créons maintenant quelques fichiers :
echo "Bonjour !" > bonjour.txt
echo "Un autre fichier" > fichier.txt
echo "### Et un dernier pour la route" > route.md
En relançant votre commande git status
, vous allez constater que git
vous indique plusieurs fichiers non-suivis.
Ajouter un fichier
Pour commencer à suivre les modifications dans un fichier (et l'ajouter
à votre prochain commit), on utilise la commande
git add nom_de_fichier
. Il est possible de stipuler plusieurs
fichiers d'un coup, en listant chaque fichier un par un (exemple :
git add bonjour.txt fichier.txt
), ou en utilisant git add .
, qui
ajoute tous les fichiers au commit.
Ajoutez l'ensemble des fichiers (avec la commande que vous souhaitez),
et relancez un git status
. Vous devriez avoir un message de Git
indiquant que les fichiers sont suivis pour le changement.
Commiter les changements
Maintenant que nous avons fait des changements, nous pouvons commiter,
c'est-à-dire fixer nos changements dans l'historique de Git. La
commande est la suivante : git commit -m "mon_message_de_commit"
.
En relançant la commande git status
, git doit vous indiquer que la
liste de changement est à nouveau vide. Cela signifie que vos
changements sont désormais présents dans Git, et qu'ils seront toujours
accessibles dans l'historique !
Voir l'historique du dépôt
Vous pouvez regarder l'historique avec la commande git log
. Ce
dernier devrait normalement afficher un seul bloc, contenant :
- Le hash du commit (un ensemble de chiffres et de lettre aléatoire permettant d'identifier le commit)
- La branche sur laquelle les changements ont été poussés
- L'auteur du commit
- La date
- Et enfin, le message de commit
Maintenant, nous allons ajouter une seconde série de modifications.
Ajoutez du contenu dans le fichier "bonjour.txt", et relancez un
git status
. Vous devriez voir les changements que vous avez
effectués.
Voir les différences sur le dépôt local
Une autre commande très utile est git diff
. Cette commande va vous
permettre de voir l'ensemble des changements effectués dans un fichier
depuis votre dernier commit. Une ligne commençant par un -
est une
ligne supprimée, une ligne commençant par un +
est une ligne ajoutée.
Si vous changez un caractère dans une ligne, la ligne entière sera
considérée comme effacée, et ajoutée.
Commitons à nouveau nos modifications (vous avez les commandes plus
haut). git log
devrait désormais afficher 2 commits !
Réparer ses erreurs
Malheur, vous vous rendez compte que vous avez fait une vilaine faute
dans un message de commit ! Heureusement, la fonction "amend" vous
permet de corriger cela sans être vu :
git commit --amend -m "Update my message !"
. Relancer la commande
git log
vous permettra de voir que le message a bien été mis à jour.
Retourner en arrière
Parfois, vous aurez envie de revenir en arrière, car vous vous rendrez
compte qu'une modification dans votre code a eu des impacts
indésirables. git revert
est là pour vous !
Exécutons la commande git revert HEAD
. Quittez vi avec :q
. Relancez
git log
: que s'est-il passé ?
Il est possible de revenir en arrière de plusieurs versions en
spécifiant le nombre de commits que l'on souhaite annuler :
git revert HEAD~2
permet ainsi de revenir en arrière de 2 commits.
Lancez la commande, et constatez les changements en explorant le contenu
de votre dépôt (avec ls
par exemple).
Maintenant, faites un dernier git revert
pour revenir à l'état
initial du dépôt.
Ignorer des fichiers
Parfois, vous souhaiterez ignorer des fichiers de votre arborescence Git
(fichier de base de données, de configuration, etc). Un moyen très
simple de le faire est d'ajouter un fichier .gitignore
à la racine de
notre projet, et d'y noter les fichiers à exclure. Par exemple :
configuration.txt
base.h2
dossier/ #Va exclure le dossier entier
*.h2 #Va exclure tout les fichiers terminant par .h2
Créez un fichier .gitignore
avec le contenu fourni ci-dessus, puis
créez un fichier "configuration.txt". Lancez un git status
. Que
constatez-vous ? Ajoutez et commitez les modifications.
Un site très utile au quotidien est
gitignore.io, qui vous permet
de générer des fichiers .gitignore
à la volée.
Gérer des branches
Les branches permettent de contenir des versions déviant de la ligne de développement principale. Cette fonctionnalité est très utilisée, notamment pour pouvoir expérimenter du code sans toucher la branche principale.
À l'initialisation, une première branche est créée. Cette branche se
nomme master
. Il est possible de voir la liste des branches avec la
commande git branch
. La branche actuelle est notée d'un astérisque.
Pour créer une branche, il existe deux options :
git branch nom_branche
, va créer une branche sans vous basculer dessus. Il faudra basculer avecgit checkout nom_branche
git checkout -b nom_branche
, va créer la branche et vous positionner dessus.
Créez une nouvelle branche experimental
, et accédez-y.
Créez un fichier experiment.txt
avec du contenu dedans (ce que vous
voulez). Commitez vos changements, et vérifiez avec git log
. Basculez
maintenant sur la branche master : git checkout master
. Relancez
git log
. Que constatez-vous ? Idem pour la commande ls
.
Comme vous pouvez le voir, ni git log
ni la commande ls
ne voient
votre fichier experiment.txt. Ce qui est normal, comme vous l'avez créé
sur une autre branche !
Fusionner des branches
Maintenant, lançons la commande git merge experimental
. Cette
commande va récupérer les changements effectués dans la branche
experimental
, et les rapatrier dans master
.
Si vous relancez ls
ou git log
, vous devriez désormais voir le
fichier experiment.txt
et son commit associé ! Nous pouvons désormais
supprimer la branche experimental
qui ne sert plus à rien.
Supprimer une branche
Pour supprimer une branche, il suffit d'utiliser la commande
git branch
avec l'argument -d
. Exemple :
git branch -d experimental
. Vous pouvez vérifier que la branche a
bien été supprimée en listant vos branches.
Gérer les conflits
Lorsque vous fusionnez des branches, il est possible que vous obteniez un conflit ; c'est-à-dire une situation que Git ne sait pas résoudre tout seul. Cela arrive notamment lorsque deux personnes modifient le même fichier sur deux branches différentes.
Créons une situation de conflit :
git checkout -b conflit
echo "bonjour" > experiment.txt
git add . && git commit -m "bonjour"
git checkout master
echo "coucou" > experiment.txt
git add . && git commit -m "coucou"
Ici, nous venons d'écrire "bonjour" dans le fichier experiment.txt
sur la branche conflit, et coucou dans le même fichier sur la branche
master. Tentons de merger notre branche : git merge conflit
.
Comme attendu, Git nous renvoie une erreur indiquant un conflit dans experiment.txt. À ce stade, il n'y a pas d'autre choix que de résoudre manuellement le conflit. Éditez le fichier experiment.txt (avec vi, nano, ou un autre éditeur de text).\ Le contenu devrait être le suivant :
<<<<<<< HEAD
coucou
=======
bonjour
>>>>>>> conflit
La structure est assez simple : la partie située entre <<<<<<< HEAD
et
=======
est issu de la branche actuelle (master), et la partie entre
=======
et >>>>>> conflit
est issu de la branche conflit. Pour
résoudre un conflit, il suffit de supprimer ces éléments, et d'arranger
comme on le veut le contenu, à savoir garder un côté ou l'autre (ou les
deux).
Ensuite, on rajoute un commit pour indiquer la résolution du conflit :
git add . && git commit -m "conflit résolu !"
. On peut alors
supprimer la branche conflit.
Dépôts distants
Pour cette partie TP, nous allons utiliser une instance de GitLab, un serveur Git très utilisé en entreprise. Rendez-vous sur https://gitlab.isima.fr/, et créez un compte avec votre adresse universitaire. (Cette instance est vidée régulièrement !).
Ensuite, créez un dépôt : cliquez sur le "+" en haut à gauche de l'interface, puis sur "New Repository". Donnez-lui un nom, et cliquez sur "Create Repository". Votre dépôt est désormais créé.
Maintenant, nous devons indiquer à notre dépôt local que nous avons une
cible distante. Pour cela, nous pouvons utiliser la commande
git remote
. Cette commande permet d'ajouter, modifier ou supprimer
des dépôts distants à synchroniser.
Lançons la commande : git remote add origin URL
. L'URL est votre
dépôt est indiquée sur l'interface de GitLab, et termine par un .git
.
Ensuite, nous allons pousser nos changements :
git push -u origin master
. Git vous demandera très probablement votre
identifiant et votre mot de passe de l'instance GitLab. Vous devriez
voir apparaître dans l'interface Web vos fichiers !
Maintenant, imaginons que vous avez deux ordinateurs, sur lequel votre
code est présent. Allez dans ~/TPGenieLog
. Nous allons cloner notre
dépôt distant. Le clone permet de récupérer une copie locale du dépôt,
pour y travailler dessus. git clone URL SecondPC
. Le dépôt git sera
cloné dans un dossier nommé SecondPC.
Dans le dossier SecondPC
, effectuez des modifications, commitez et
poussez les modifications.
Retournez dans le dossier ~/TPGenieLog/Git
: vos modifications
apportées ne sont pas encore présentes. Vous pouvez les récupérer avec
git pull
.
Conclusion
Nous avons vu dans ce TP quelques possibilités offertes par Git. Cependant, nous sommes loin d'avoir tout vu ! Git dispose en effet de commandes très poussées permettant d'effectuer de nombreuses actions (rebase, cherry pick, etc).
Grâce à ce TP, vous êtes désormais en mesure d'utiliser Git dans vos projets universitaires et personnels.
Si vous souhaitez héberger du code, n'hésitez pas à aller voir les sites GitHub et GitLab, qui permettent de créer gratuitement des dépôts Git.