Plan pour un atelier sur la gestion de versions, en prenant Git comme référentiel.

Sommaire

Les systèmes de gestion de versions (VCS)

  • Qu'est-ce qu'un VCS ?
  • À quoi ça sert ?
    • Gestion de code source
    • Documents littéraires
    • Gestion de configuration
  • Principes majeurs :
    • Le dépôt
    • La copie de travail
    • Les commits
    • Les merges et les conflits
  • VCS centralisés (CVCS) et décentralisés (DVCS).
    • Principe des CVCS (exemple de Subversion).
    • Principe comparé des DVCS (exemple de Git).
  • Comparatif des deux solutions.
    • Les CVCS sont plus simples à utiliser de manière basique.
    • Différences en termes d'espace disque, pour différents usages :
      • Subversion stocke la dernière révision récupérée sur le serveur et une copie de travail −> avantage pour des fichiers binaires changeant souvent et les très gros dépôts.
      • Git stocke chaque version de chaque fichier, sous forme compressée.
    • Différences en termes de fonctionnalités, à peu près pour les mêmes raisons :
      • Hors ligne, Subversion peut comparer les modifications de la copie de travail par rapport à la dernière version récupérée du serveur. Impossible d'avoir l'historique hors ligne.
      • Git peut tout comparer, tout afficher, etc.
      • Git permet de réorganiser et modifier des commits, faire facilement des branches dans tous les sens, etc.

Au cœur de Git

  • Organisation interne d'un dépôt Git : présentation du système de fichier copy on write qu'est Git.
  • Les types d'objets :
    • blob,
    • tree,
    • commit,
    • tag.
  • L'index (ou staging area)
    • Commiter seulement certains fichiers.
    • Commiter des morceaux (hunks) de fichiers.
  • Modifier l'historique
    • Modifier l'ordre des commits.
    • Séparer un commit en plusieurs commits plus petits.
    • Combiner plusieurs commits en un seul commit plus gros.
    • Modifier des fichiers dans un commit, modifier un message de commit.

Il est important d'insister sur le fait que la manière de travailler avec Git est (ou peut être) radicalement différente de celle que l'on emploie avec un CVCS, justement parce qu'il est possible de modifier l'historique : on peut se permettre de commiter souvent, de faire des erreurs que l'on va corriger ensuite, etc.

Travaux pratiques

L'idée est de reprendre les éléments théoriques exposés plus haut et de voir concrètement comment on traite chaque problématique.

Création d'un dépôt

À partir de rien :

git init nom_du_dépôt

ou :

mkdir nom_du_dépôt
cd nom_du_dépôt
git init

À partir d'un dépôt existant :

git clone adresse_du_dépôt

Enregistrer des modifications

Ajouter un fichier non versionné, ou marquer pour commit un fichier modifié :

git add

On peut aussi ajouter des fichiers en mode interactif, y compris ajouter partiellement des modifications sur un fichier, en utilisant l'option -i :

git add -i

Démarquer un fichier :

git reset

Supprimer un fichier (utiliser --cached pour ne pas le supprimer de la copie de travail) :

git rm

Commiter les modifications enregistrées :

git commit

Commiter toutes les modifications :

git commit -a

Annuler les modifications sur le fichier « fichier » :

git checkout fichier

Observer les changements

Quelles sont les modifications de la copie de travail ?

git status

Voir l'historique de la branche courante :

git log

Voir l'historique de tout le dépôt :

git log --all

Essayez aussi les options suivantes de git log : -p, --stat, --pretty=oneline, --graph

Interfaces graphiques

Deux interfaces graphiques intéressantes sont fournies avec Git. Gitk permet de visualiser l'historique des changements sur une branche, ou sur toutes les branches du dépôt avec l'option --all :

gitk --all

Git GUI permet de préparer les commits en mode graphique, en visualisant les changements sur chaque fichier. Il est par exemple plus pratique que git add -i pour marquer partiellement un fichier pour commit.

git gui

Branches

Note : en faisant des expériences sur les branches, n'hésitez pas à observer le dépôt avec gitk --all ou git log --graph --pretty=oneline --all

Voir la liste des branches locales :

git branch

Voir la liste des branches distantes :

git branch -r

Voir la liste de toutes les branches :

git branch -a

Créer une nouvelle branche au même point que la branche courante :

git branch nom_de_la_branche

Changer de branche (si l'on passe l'identifiant d'un commit, une branche temporaire sera créée) :

git checkout branche_ou_commit

Créer une nouvelle branche (éventuellement à partir d'une autre branche ou d'un commit), et se placer dessus :

git checkout -b nom_de_la_branche [ branche_ou_commit ]

Ajouter les commits de la branche « dev » à la branche « master » de manière à avoir un historique linéaire (git se placera automatiquement sur la branche master) :

git rebase dev master

Merger la branche dev dans la branche master (en créant un commit de merge ayant deux parents) :

git checkout master
git merge dev

Supprimer une branche :

git branch -d nom_de_la_branche

Sauvegarder les changements de manière temporaire (stash)

Pour nettoyer la copie de travail de tout changement (par exemple pour permettre le changement de branche) :

git stash

Les modifications seront sauvegardées dans une sorte de commit temporaire (visible par exemple dans gitk --all).

Il est possible d'appeler git stash plusieurs fois. On peut visualiser la liste des changements sauvegardés :

git stash list

Pour réappliquer la dernière sauvegarde, on utilise git stash apply (ce qui conservera la sauvegarde) ou git stash pop (ce qui la supprimera), éventuellement avec l'option --index pour restaurer l'index (changements marqués pour commit) :

git stash pop
git stash pop --index
git stash apply
git stash apply --index

Pour supprimer la dernière sauvegarde sans l'appliquer :

git stash drop

Pour appliquer ou supprimer non pas le dernier état sauvegardé mais une sauvegarde antérieure, il faut noter son numéro dans git stash list et l'indiquer à la suite de la commande. Par exemple, pour supprimer l'état numéro 1 (le deuxième plus récent donc) :

git stash drop stash@\{1\}

Modifier l'historique du dépôt

Avertissement : il est déconseillé de modifier un dépôt public, à moins d'être sûr que personne ne travaille dessus. Toutes les personnes ayant obtenu une copie du dépôt devront le copier à nouveau.

Il est possible de modifier le dernier commit grâce à l'option --amend de git commit :

git commit --amend

Il faut préalablement avoir apporté et marqué des modifications à la copie de travail (git add, git rm…). Si ce n'est pas le cas, git commit --amend permettra simplement de modifier le message de commit.

Il est également possible de modifier l'ordre des commits, de modifier des commits antérieurs, de fusionner ou diviser des commits, etc. La commande maîtresse est alors git rebase avec l'option -i ; il faut indiquer la somme de contrôle SHA-1 du commit parent du premier commit sur lequel on veut travailler.

git rebase -i SHA1

Git nous présentera alors un éditeur de texte, et nous permettra de marquer les modifications que l'on veut effectuer (des indications sont données en commentaire dans ce fichier texte).

Par exemple, pour modifier un commit, il faudra le marquer « edit » (ou simplement « e »), enregistrer le fichier texte et quitter l'éditeur. On utilisera ensuite git commit --amend comme vu précédemment, puis git rebase --continue pour terminer.