FLASH INFORMATIQUE FI



Git, un logiciel de gestion de versions décentralisée


Cet article présente Git, le logiciel de gestion de versions décentralisée (DVCS) créé par Linus Torvalds pour le développement du noyau Linux. Depuis, il est utilisé par des milliers de projets libres dans le monde.



This article presents Git, the distributed version control system (DVCS) that Linus Torvalds created for the Linux kernel development. Since then, it has been adopted by thousands of free software projects around the world.


Didier RABOUD


Fiche descriptive

Un peu d’histoire

Git est un logiciel de gestion de versions (Version Control System - VCS). En bref, c’est un logiciel qui permet de stocker un ensemble de fichiers en conservant la chronologie et l’attribution de toutes les modifications qui ont été effectuées dessus. Le domaine le plus courant dans lequel ce type de logiciel est utilisé est le développement logiciel : l’utilisation d’un tel logiciel pour gérer une arborescence de fichiers permet de travailler à plusieurs, d’enregistrer la chronologie et l’attribution de toutes les modifications, de figer des versions données, de revenir à n’importe quelle version antérieure, etc. Si les logiciels de gestion de versions sont particulièrement adaptés pour la gestion de codes sources, ils le sont tout autant pour d’autres types de fichiers texte [1] : fichiers LaTex pour la rédaction d’un travail de Master, recettes de cuisine, journal intime, etc. Étant donnée l’importance évidente de ce genre de système pour le développement logiciel, leur histoire remonte aux origines de l’informatique : elle commence en 1972 par SCCS et est passée depuis par les plus connus : CVS, Subversion et consorts.
Git a été conçu initialement par Linus Torvalds en avril 2005 [2] avec pour but le remplacement du système BitKeeper pour le développement du noyau Linux. Ce logiciel privateur était en effet utilisé par les développeurs du noyau jusqu’en 2005 lorsque le propriétaire de BitKeeper retira les droits d’utilisation jusqu’alors accordés auxdits développeurs  [3]. Ainsi, Torvalds commença le développement de Git le 3 avril 2005, dès le 7 avril le code de Git était maintenu dans Git lui-même, et la version 2.6.12 du noyau, sortie le 16 juin, avait déjà été gérée à l’aide de Git. Le 16 juillet, après avoir posé les bases du logiciel, il se retira de la gestion du projet. La  version 1.0  de Git sortit le 21 décembre de la même année et le développement se poursuit depuis lors.

Installation de Git

Git est disponible sur toutes les distributions libres, où le paquet s’appelle simplement git, parfois git-core [4]. Le site internet de Git  pointe vers des installateurs pour les autres systèmes d’exploitation : un *.dmg pour toutes les versions de Mac OS ; l’utilisation de l’un des deux portages existants (msysgit ou Git/Cygwin) pour Windows.

À l’aide

Notons en préambule que Git est extrêmement bien documenté par les systèmes usuels du monde UNIX : man 7 gittutorial et man 1 git sont de bons points de départ. Notons également que toutes les sous-commandes de git ont une page de man accessible comme suit :

$ git help <commande>

Versionner la fondue

Afin de comprendre à quoi un système de gestion de versions peut bien servir, le plus simple est d’essayer. Partant d’une version installée de Git , créons notre premier dépôt git :

$ mkdir recettes
$ cd recettes
$ git init # Crée un dépôt vide

Ce dépôt existe maintenant dans le dossier recettes : un sous-dossier .git/ (qui contient toutes les méta-informations du dépôt) y a été créé. Notons que contrairement aux gestionnaires de versions centralisés, Git ne fait aucune différence entre dépôts distants et extraits locaux : ce dépôt n’est local que parce qu’il est... ici.
Éditons ensuite un premier fichier nommé fondue et ajoutons-le à notre gestionnaire de versions :

$ cat > fondue <<EOF
==== Recette de la fondue ====
== Matériel ==
Vin, fromage, caquelon, fourchettes, alcool à brûler.
EOF
$ git add . # Ajoute l&#8217;entier du dossier à l&#8217;index
$ git commit -m "Ma recette de la fondue"

Et voilà, notre recette de fondue est conservée dans le gestionnaire de versions. Vérifions ça :

$ git show # Affiche la dernière révision
commit 04d1d921281b4fc81e9c60266efb666fbb1c0bd9
Author: Didier Raboud <didier.raboud@epfl.ch>
Date:   Tue May 31 18:06:25 2011 +0200

   Ma recette de fondue

diff --git a/fondue b/fondue
new file mode 100644
index 0000000..28c9ddc
--- /dev/null
+++ b/fondue
@@ -0,0 +1,3 @@
+==== Recette de la fondue ====
+== Matériel ==
+Vin, fromage, caquelon, fourchettes, alcool à brûler.

Ce que contient une révision

Décortiquons cette première révision (commit dans le jargon Git).

Le hash

commit 04d1d921281b4fc81e9c60266efb666fbb1c0bd9

En premier, nous trouvons le hash de la révision : ce hash (qui est une empreinte cryptographique SHA1, de 160 bits, formatés en hexadécimal) est un identifiant pseudo-unique [5] déterminé par le contenu de la révision. On s’y réfère habituellement à l’aide des 7 premiers digits hexadécimaux (04d1d92 dans ce cas), mais c’est l’entier de l’empreinte qui identifie la révision de manière univoque.

L’auteur

Author: Didier Raboud <didier.raboud@epfl.ch>

Ensuite, nous trouvons une référence de l’auteur de la révision. C’est un aspect évidemment central des gestionnaires de version, qui permet d’identifier les auteurs de chaque contribution. Notons au passage que la personne qui crée la révision peut être différente de l’auteur : c’est alors le champ Committer : qui contient cette information. Ceci permet une granularité plus fine dans l’attribution des contributions.

L’empreinte horaire

Date:   Tue May 31 18:06:25 2011 +0200

Les révisions contiennent toutes une empreinte horaire, dans un format exhaustif, qui contient l’indication du fuseau horaire.

Le message de révision / description

Ma recette de fondue

Chaque révision est accompagnée d’un message qui décrit supposément les modifications qu’elle contient. À ce sujet, les bonnes pratiques en vigueur suggèrent de faire de la première ligne un résumé du message tenant en 70 caractères. Le nombre de lignes du message n’étant pas limité, les messages de révision du noyau Linux sont régulièrement très verbeux, ce qui facilite leur analyse [6].

Le contenu de la révision

diff --git a/fondue b/fondue
new file mode 100644
index 0000000..28c9ddc
--- /dev/null
+++ b/fondue
@@ -0,0 +1,3 @@
+==== Recette de la fondue ====
+== Matériel ==
+Vin, fromage, caquelon, fourchettes, alcool à brûler.

On peut voir ici le contenu de la révision : c’est un diff entre deux versions d’un même fichier. Une révision peut contenir des modifications pour différents fichiers, à des endroits différents de l’arborescence. Ce qu’il importe de bien comprendre pour comprendre Git, c’est que contrairement à d’autres systèmes de gestion de versions, Git s’intéresse au contenu, pas aux fichiers (ni à leurs noms). Ainsi, contrairement à CVS ou Subversion par exemple, il est possible de copier ou renommer des fichiers sans que leurs historiques respectifs ne soient perdus.
Notons rapidement la ligne index, qui indique le trajet que la révision fait parcourir au contenu du fichier : elle amène le contenu du fichier fondue de l’objet 0000000... [7] à l’objet 28c9ddc... L’histoire d’un objet est enregistrée dans chaque révision.

Ajouter le pain

Chacun ayant évidemment remarqué l’absence de pain dans la recette, modifions maintenant notre recette de fondue :

$ sed -i -e &#8216;s/fromage,/& des croissants,/g&#8217; fondue

Sur les fichiers dont Git se préoccupe, on peut maintenant afficher les différences entre la dernière révision et l’état courant  [8] :

$ git diff
diff --git a/fondue b/fondue
index 28c9ddc..0700c91 100644
--- a/fondue
+++ b/fondue
@@ -1,3 +1,3 @@
==== Recette de la fondue ====
== Matériel ==
-Vin, fromage, caquelon, fourchettes, alcool à brûler.
+Vin, fromage, des croissants, caquelon, fourchettes, alcool à brûler.

Comment ça des croissants  ? De toute évidence, cette modification n’est pas correcte. Ramenons le fichier fondue à la version de la dernière révision :

$ git checkout HEAD fondue

Cette commande sort de la révision HEAD (qui est un pointeur vers la dernière révision) le fichier fondue. git checkout permet d’extraire un fichier de n’importe laquelle de ses versions passées [9]. Nous disions donc du pain :

$ sed -i -e &#8216;s/fromage,/& du pain blanc,/g&#8217; fondue
$ git diff

Si le contenu du diff nous plaît, nous pouvons maintenant préparer notre nouvelle révision, en ajoutant le nouvel état d’un fichier à l’index, qui est une sorte de zone franche où la prochaine révision se prépare :

$ git add fondue

Notons maintenant que le résultat de git diff est vide, parce que toutes les différences par rapport à la dernière révision sont dans l’index. Ajoutons une recette de papet vaudois  [10] et observons maintenant l’état de notre dépôt :

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   fondue
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       papet

On peut ici constater que Git est verbeux sur les options qui se présentent à nous : on peut ainsi ajouter papet à l’index ou en retirer fondue. Regardons le contenu de notre index :

$ git diff --staged

Si le contenu de l’index nous plaît, nous pouvons maintenant créer une nouvelle révision avec son contenu [11] :

$ git commit -m "Ne pas oublier le pain."

Nous avons donc maintenant une chaîne de deux révisions :

$ git log
commit 8d79c7102acd129807d6dfaf652bd50819ef00d9
Author: Didier Raboud <didier.raboud@epfl.ch>
Date:   Tue May 31 20:24:31 2011 +0200

   Ne pas oublier le pain.

commit 04d1d921281b4fc81e9c60266efb666fbb1c0bd9
Author: Didier Raboud <didier.raboud@epfl.ch>
Date:   Tue May 31 18:06:25 2011 +0200

  Ma recette de fondue

Séquence de révisions


PNG - 73.3 ko
gitk gère les recettes

Notons ici que les révisions Git ne portent aucun numéro (contrairement aux révisions Subversion ou Bazaar). Ce qui pourrait paraître désarçonnant de prime abord est en fait le coeur de la force de Git : chaque révision contenant les références de chacun de ses parent [12], elle est une référence unique de l’état d’une arborescence et de l’historique y menant.
Notons également que contrairement aux gestionnaires de versions centralisés, Git ne nécessite aucune connexion vers un quelconque dépôt centralisé : chaque dépôt peut vivre sa vie indépendamment et chaque dépôt de devient central ou de référence que par l’usage qu’on peut en faire.

Visualisation de l’arbre de son dépôt

Il existe plusieurs outils pour visualiser l’historique d’un dépôt : gitk et qgit sont les plus connus. Ils permettent d’effectuer quelques manipulations simples du dépôt.



Ce qu’un dépôt Git a dans les tripes

Pour ceux qui souhaiteraient comprendre la plomberie de Git, voici un aperçu de sa tuyauterie : Git stocke quatre types d’objets, qui sont tous observables à l’aide de la commande $ git show —pretty=raw

blob

Un blob est un objet qui stocke des contenus de fichiers (des données) et strictement rien d’autre. Dans notre cas, le blob de destination vers lequel notre révision 04d1d92 a amené notre fichier fondue est 28c9ddc :

$ git show 28c9ddc
==== Recette de la fondue ====
== Matériel ==
Vin, fromage, caquelon, fourchettes, alcool à brûler.

tree

Un objet tree se comporte comme un dossier : il référence d’autres tree et/ou des blobs (soient des sous-dossiers ou des fichiers).

commit

Le commit pointe vers un seul objet tree et le marque comme l’état du dépôt à un certain instant. Il contient des méta-informations au sujet de l’instant, de l’auteur des modifications depuis le dernier commit, un pointeur vers ledit dernier commit, etc.

tag

Le tag est une manière de marquer un commit comme étant spécial d’une manière où d’une autre. Les tags sont nommés et ils peuvent également contenir des méta-informations telles que description, signature numérique, etc.
Pour plus de précisions, lire




Les branches

La fonctionnalité centrale [13] des gestionnaires de versions (décentralisé ou non) est la possibilité de créer des branches. Dans le jargon des gestionnaires de versions, une branche est un espace de développement dans lequel il est possible de faire des modifications (intrusives, complexes, ...) sans affecter l’état du tronc (l’espace de développement principal). Une fois que les modifications sont considérées comme satisfaisantes, on les intègre généralement au tronc. Notons d’abord que la branche par défaut de Git est toujours master [14]. Git ne faisant aucune différence technique entre les branches, celles-ci n’ont que l’usage que l’on veut bien en faire. L’architecture de Git rend la création de branches extrêmement peu coûteuse et simple : créons par exemple une branche experimental à partir de la révision courante :

$ git branch experimental

Avant de faire des changements dans cette branche, il faut indiquer à Git la branche dans laquelle nous voulons travailler  [15] :

$ git checkout experimental

Nous pouvons maintenant travailler sur une fondue expérimentale. Notons que la branche ne s’est pas créée dans un autre dossier (à la Subversion ou Bazaar), mais qu’elle existe dans la même arborescence et que la branche émerge simplement dans le dossier courant. On peut ainsi naviguer entre les différentes branches sans changer de dossier.
Lors de la création de nouvelles révisions, celles-ci vont s’empiler à la suite de la révision d’origine (celle qui était pointée par master lorsque la création de la branche a eu lieu) et la dernière sera toujours pointée par la référence experimental. Une branche Git est donc simplement un nom qui pointe vers une révision.
Une fois que les changements dans la branche experimental sont satisfaisants, il est possible de les intégrer à master (qui a pu évoluer entre temps) comme suit :

$ git checkout master # On revient à master
$ git merge experimental

Si les modifications faites dans la branche experimental n’entrent pas en conflit avec celles faites dans la branche master, alors le merge sera dit trivial. Sinon, une résolution manuelle sera proposée (mais elle sort du cadre de cet article).

La fondue se partage

Maintenant que nous savons créer et faire évoluer nos recettes, il nous faut explorer la dimension distribuée de Git. Premièrement, notons que la notion de serveur central ou de référence n’existe pas dans Git (ni dans aucun DVCS d’ailleurs, comme leur nom l’indique). Un dépôt ne devient central ou important que par l’usage qu’on en fait : dans le cadre du développement du noyau Linux par exemple, c’est la copie de Linus Torvalds  [16] qui est considérée comme étant la copie de référence, mais c’est là une caractéristique organisationnelle et non technique.
Deuxièmement, il faut savoir que Git a été conçu pour être compatible avec les protocoles existants à l’époque. Ainsi il est possible d’utiliser les fonctions de partage de Git à travers HTTP, FTP, et rsync par exemple : il est également possible d’utiliser le protocole de Git à travers ssh ou un simple socket. git-daemon permet la publication d’un dépôt sur le port 9418. La mise en place de dépôts accessibles dépassant le cadre du présent exposé [17], la démonstration se fera avec plusieurs dépôts locaux.

Améliorer la fondue

Tout d’abord, copions notre dépôt fondue :

$ cd ..
$ git clone recettes recettes-georges
$ cd recettes-georges

Ce nouveau dossier fondue-savoyarde est un clone de fondue, il contient donc le même arbre de révisions. Changeons maintenant d’identité pour l’exemple :

$ git config user.name &#8216;Georges Tartempion&#8217;
$ git config user.email &#8216;georges.tartempion@example.com&#8217;

Et préparons une modification et une nouvelle révision [18] :

$ sed -i -e &#8216;s|fromage,|mélange de fromages (comté, beaufort, gruyère),|g&#8217; fondue
$ git commit -m "Précisions au sujet du mélange de fromages" fondue

Georges ne pouvant pousser sa nouvelle révision vers le dépôt recettes, il va devoir communiquer sa proposition au gestionnaire dudit dépôt qui va pouvoir tirer cette nouvelle révision. Retournons donc dans recettes et créons une nouvelle référence vers un dépôt distant, que nous appellerons georges :

$ cd ../recettes
$ git remote add georges ../recettes-georges

Mettons maintenant à jour nos références du dépôt georges :

$ git fetch georges
From ../recettes-georges
* [new branch]      master     -> georges/master

Une nouvelle pseudo-branche s’est ainsi créée dans le dépôt recettes : georges/master. Avant d’accepter ces modifications, il nous faut les observer. Git offre des dizaines de manières de visualiser les modifications entre deux branches, en voici une :

$ git log -U master..georges/master

Dans l’hypothèse  [19] où ces modifications nous plairaient, nous pouvons maintenant les intégrer dans notre branche master :

$ git merge georges/master
Updating 8d79c71..dd908c7
Fast-forward
fondue |    2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

Le fast-forward nous indiquant ici que l’intégration des changements de la branche georges/master a été linéaire (nous n’avions pas fait de changements sur le dépôt fondue depuis que Georges avait créé son clone) [20].
Nous avons ainsi abordé les quelques aspects essentiels de Git : la création de révisions et de branches ainsi que l’interaction entre plusieurs dépôts. Heureusement, la documentation officielle de Git est abondante et permet rapidement de faire usage des concepts plus avancés de Git.

Conclusion

Git est un outil extrêmement puissant, avec lequel il est possible de faire énormément de choses [[Git a plus de 140 sous-commandes et il existe nombre de plugins qui étendent son champ d’application.
, des plus basiques aux plus complexes. Son architecture a été pensée dès sa création pour permettre des collaborations à large échelle et son efficacité est prouvée depuis plus de 5 ans par son utilisation dans un des plus gros projets logiciels du monde libre ; le noyau Linux.
Git peut paraître déroutant au premier abord parce qu’il n’utilise pas nombre des concepts principaux des gestionnaires de versions centralisés : plus de numéro révision, pas d’ordre de révisions explicite, pas de dépôt central faisant office de référence, plus d’obligation d’être en ligne pour créer des révisions, etc. Mais l’abandon de tous ces concepts s’est fait pour des raisons architecturales, pour créer un système universel, cohérent et fonctionnel.
Ainsi Git est un logiciel de gestion de versions qui est distribué, rapide, qui économise la place disque, dans lequel tout est local, dans lequel la création de branches locales est rapide et économe. Finalement, Git n’impose ni manières de travailler ni dénominations spécifiques : son universalité fait sa force.


Article du FI-EPFL 2011 sous licence CC BY-SA 3.0 / D. Raboud



Glossaire

Gite :
, logiciel libre de gestion de versions décentralisée créé par Linus Torvalds, le créateur du noyau Linux, et distribué sous la GNU GPL version 2... Pour la petite histoire, le mot provient de l’argot anglais dans lequel il signifie plus ou moins connard. W
version 1.0 :
a version 1.0 d’un logiciel est usuellement considérée comme étant prête pour un large usage ; soit un ensemble de caractéristiques donné, invariant et supposément sans anomalies.
W= tiré de Wikipédia

[1] La gestion de version de fichiers binaires est généralement possible mais peu efficace.

[2] en.wikipedia.org/wiki/Git_(software) raconte bien plus sur l’histoire et les fondements de Git qu’il ne serait pertinent d’en raconter ici.

[3] Ce pour des raisons diverses qu’il serait trop long (et inutile) d’exposer ici. Retenons au passage le risque que peut faire peser l’utilisation d’un logiciel privateur sur les développements futurs d’un projet libre.

[4] Ceci parce que les GNU Interactive Tools utilisaient déjà le nom de Git lors de la création du Git qui nous concerne ; ils sont maintenant disponibles sous le nom de gnuit.

[5] Selon fr.wikipedia.org/wiki/SHA-1, en l’état actuel des recherches, une attaque visant à déterminer une collision dans SHA-1 prendrait 2 63 opérations.

[6] Il est également à noter que d’autres bonnes pratiques sont également en vigueur, en particulier pour le développement du noyau Linux ; par exemple l’ajout progressif au fil des vérifications par les différents responsables de pseudo-entêtes Signed-off-by : (qui s’accumulent à la fin du message) qui permettent d’attribuer des garanties à une révision donnée.

[7] soit rien.

[8] On observera la valeur temporaire de l’index (0700c91), que Git a créé pour nous.

[9] Notons qu’on aurait ici pu se passer de mentionner explicitement HEAD.

[10] Dont je vous épargne ici le détail.

[11]  git commit appelé sans son option -m lance un éditeur qui permet d’éditer le message de révision.

[12] Les révisions normales n’ont qu’un seul parent, mais les merges en ont plus.

[13] Et dont l’implémentation est souvent la différence caractéristique entre les différents (D)VCS.

[14] La flexibilité de Git permet évidemment d’utiliser un autre nom.

[15] Il aurait été possible de créer la branche et s’y rendre d’un seul coup : $ git checkout -b experimental.

[16] git ://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git que chacun peut cloner.

[17] Git se base sur le système de fichiers pour gérer les droits de ses dépôts ; ainsi mettre en place un dépôt sur une machine distante nécessite de gérer correctement les droits des différents utilisateurs. Ce n’est pas compliqué, mais ce n’est plus Git.- :)

[18] Notons ici la variante d’utilisation de git commit qui, en précisant les fichiers concernés, évite la préparation préalable de l’index.

[19] Peu probable certes. Du comté dans la fondue  ?

[20]  git pull permet de faire la même chose que la succession de git fetch et git merge.



Cherchez ...

- dans tous les Flash informatique
(entre 1986 et 2001: seulement sur les titres et auteurs)
- par mot-clé

Avertissement

Cette page est un article d'une publication de l'EPFL.
Le contenu et certains liens ne sont peut-être plus d'actualité.

Responsabilité

Les articles n'engagent que leurs auteurs, sauf ceux qui concernent de façon évidente des prestations officielles (sous la responsabilité du DIT ou d'autres entités). Toute reproduction, même partielle, n'est autorisée qu'avec l'accord de la rédaction et des auteurs.


Archives sur clé USB

Le Flash informatique ne paraîtra plus. Le dernier numéro est daté de décembre 2013.

Taguage des articles

Depuis 2010, pour aider le lecteur, les articles sont taggués:
  •   tout public
    que vous soyiez utilisateur occasionnel du PC familial, ou bien simplement propriétaire d'un iPhone, lisez l'article marqué tout public, vous y apprendrez plein de choses qui vous permettront de mieux appréhender ces technologies qui envahissent votre quotidien
  •   public averti
    l'article parle de concepts techniques, mais à la portée de toute personne intéressée par les dessous des nouvelles technologies
  •   expert
    le sujet abordé n'intéresse que peu de lecteurs, mais ceux-là seront ravis d'approfondir un thème, d'en savoir plus sur un nouveau langage.