Merge pull request #28 from VBoden/master
[gitmagic.git] / fr / secrets.txt
blob1bdc60396639c5b78ea63d3e10e3f8df3869de4c
1 // -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*-
2 == Les secrets révélés ==
4 Nous allons jeter un œil sous le capot pour comprendre comment Git réalise ses
5 miracles. Je passerai sous silence la plupart des détails. Pour des
6 explications plus détaillées, référez-vous au
7 http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[manuel
8 utilisateur].
10 === L'invisibilité ===
12 Comment fait Git pour être si discret ? Mis à part lorsque vous faites des
13 commits et des fusions, vous pouvez travailler comme si la gestion de versions
14 n'existait pas. Et c'est lorsque vous en avez besoin que vous êtes content de
15 voir que Git veillait sur vous tout le temps.
17 D'autres systèmes de gestion de versions vous mettent constamment aux prises
18 avec de la paperasserie et de la bureaucratie. Les fichiers sont en lecture
19 seule jusqu'à l'obtention depuis un serveur central du droit d'édition de
20 tel ou tel fichier. Les commandes les plus basiques voient leurs performances
21 s'écrouler au fur et à mesure que le nombre d'utilisateurs augmente. Le travail
22 s'arrête dès lors que le réseau ou le serveur central est en panne.
24 À l'inverse, Git conserve tout l'historique de votre projet dans le
25 sous-dossier `.git` de votre dossier de travail. C'est votre propre copie de
26 l'historique et vous pouvez donc rester déconnecté tant que vous ne voulez pas
27 communiquer avec les autres. Vous conservez un contrôle total sur le sort de
28 vos fichiers puisque Git peut aisément les recréer à tout moment à partir de
29 l'un des états enregistrés dans `.git`.
31 === L'intégrité ===
33 La plupart des gens associent la cryptographie à la conservation du secret des
34 informations mais l'un de ses buts tout aussi important est de conserver
35 l'intégrité de ces informations. Un usage approprié des fonctions de hachage
36 cryptographiques (celles qui calculent l'empreinte d'un document) permet
37 d'empêcher la corruption accidentelle ou malicieuse des données.
39 Une empreinte SHA1 peut être vue comme un nombre de 160 bits identifiant de
40 manière unique n'importe quelle suite d'octets que vous rencontrerez dans votre
41 vie. On peut même aller plus loin : c'est vrai pour toutes les suites d'octets
42 que les humains utiliseront sur plusieurs générations.
44 Comme une empreinte SHA1 est elle-même une suite d'octets, nous pouvons
45 calculer l'empreinte d'une suite de caractères contenant d'autres
46 empreintes. Cette simple observation est étonnamment utile (cherchez par
47 exemple 'hash chain'). Nous verrons plus tard comment Git utilise cela pour
48 garantir efficacement l'intégrité des données.
50 En bref, Git conserve vos données dans le sous-dossier `.git/objects` mais à
51 la place des noms de fichiers normaux, vous n'y trouverez que des ID. En
52 utilisant ces ID comme noms de fichiers et grâce à quelques astucieux fichiers
53 de verrouillage et d'horodatage, Git transforme un simple système de fichiers
54 en une base de données efficace et robuste.
56 === L'intelligence ===
58 Comment fait Git pour savoir que vous avez renommé un fichier même si vous ne
59 lui avez pas dit explicitement ? Bien sûr, vous pouvez utiliser *git mv* mais
60 c'est exactement la même chose que de faire *git rm* suivi par *git add*.
62 Git a des heuristiques pour débusquer les changements de noms et les copies
63 entre les versions successives. En fait, il peut même détecter les bouts de
64 code qui ont été déplacés ou copiés d'un fichier à un autre ! Bien que ne
65 couvrant pas tous les cas, cela marche déjà très bien et cette fonctionnalité
66 est encore en cours d'amélioration. Si cela échoue pour vous, essayez les
67 options activant des méthodes de détection de copie plus coûteuses et envisager
68 de faire une mise à jour.
70 === L'indexation ===
72 Pour chaque fichier suivi, Git mémorise des informations, telles que sa taille
73 et ses dates de création et de dernières modifications, dans un fichier appelé
74 'index'. Pour déterminer si un fichier a changé, Git compare son état courant
75 avec ce qu'il a mémorisé dans l'index. Si cela correspond alors Git n'a pas
76 besoin de relire le fichier.
78 Puisque les appels à 'stat' sont considérablement plus rapides que la lecture
79 des fichiers, si vous n'avez modifié que quelques fichiers, Git peut déterminer
80 son état en très peu de temps.
82 Nous avons dit plus tôt que l'index était une aire d'assemblage. Comment se
83 peut-il qu'un simple fichier contant quelques informations sur les fichiers
84 soit une aire d'assemblage ? Parce que la commande add ajoute les fichiers à la
85 base de données de Git et met à jour l'index avec leurs informations alors que
86 la commande commit, sans option, crée une nouvelle version basée uniquement sur
87 cet index et les fichiers déjà inclus dans la base de données.
89 === Les origines de Git ===
91 Ce http://lkml.org/lkml/2005/4/6/121[message de la Mailing List du noyau Linux]
92 décrit l'enchaînement des évènements ayant mené à Git. L'ensemble de l'enfilade
93 est un site archéologique fascinant pour les historiens de Git.
95 === La base d'objets ===
97 Chacune des versions de vos données est conservée dans la base d'objets
98 ('object database') qui réside dans le sous-dossier `.git/objects` ; le
99 reste du contenu du dossier `.git` représente moins de données : l'index, le
100 nom des branches, les tags, les options de configuration, les logs,
101 l'emplacement actuel de HEAD, et ainsi de suite. La base d'objets est simple
102 mais élégante et constitue la source de la puissance de Git.
104 Chaque fichier dans `.git/objects` est un objet. Il y a trois sortes d'objets
105 qui nous concerne : les `blobs`, les arbres (`trees`) et les `commits`.
107 === Les blobs ===
109 Tout d'abord, faisons un peu de magie. Choisissez un nom de
110 fichier... n'importe quel nom de fichier ! Puis dans un dossier vide, faites
111 (en remplaçant `VOTRE_NOM_DE_FICHIER` par le nom que vous avez choisi) :
113  $ echo joli > VOTRE_NOM_DE_FICHIER
114  $ git init
115  $ git add .
116  $ find .git/objects -type f
118 Vous verrez +.git/objects/06/80f15d4cb13a09f600a25b84eae36506167970+.
120 Comment puis-je le savoir sans connaître le nom de fichier que vous avez
121 choisi ? Tout simplement parce que l'empreinte SHA1 de :
123  "blob" SP "5" NUL "joli" LF
125 est 0680f15d4cb13a09f600a25b84eae36506167970. Où SP est un espace, NUL est
126 l'octet de valeur nulle et LF est un passage à la ligne. Vous pouvez vérifier
127 cela en tapant :
129   $ printf "blob 5\000joli\n" | sha1sum
131 Git utilise un classement par contenu : les fichiers ne sont pas stockés selon
132 leur nom mais selon l'empreinte des données qu'ils contiennent, dans un fichier
133 que nous appelons un objet 'blob'. Nous pouvons considérer l'empreinte comme un
134 ID unique du contenu d'un fichier. Donc nous pouvons retrouver un fichier par
135 son contenu. La chaîne initiale `blob 5` est simplement un entête indiquant le
136 type de l'objet et sa longueur en octets ; cela simplifie le classement
137 interne.
139 Je peux donc aisément prédire ce que vous voyez. Le nom du fichier ne compte
140 pas : pour construire l'objet blob, seules comptent les données stockées dans
141 le fichier.
143 Peut-être vous demandez-vous ce qui se produit pour des fichiers ayant le même
144 contenu. Essayez en créant des copies de votre premier fichier, avec des noms
145 quelconques. Le contenu de +.git/objects+ reste le même quel que soit le nombre
146 de copies que vous avez ajoutées. Git ne stocke le contenu qu'une seule fois.
148 À propos, les fichiers dans +.git/objects+ sont compressés par zlib et, par
149 conséquent, vous ne pouvez pas en consulter le contenu directement. Passez-les
150 au travers du filtre http://www.zlib.net/zpipe.c[zpipe -d] ou tapez :
152  $ git cat-file -p 0680f15d4cb13a09f600a25b84eae36506167970
154 qui affiche proprement l'objet choisi.
156 === Les arbres (`trees`) ===
158 Mais que deviennent les noms des fichiers ? Ils doivent bien être stockés
159 quelque part à un moment. Git se préoccupe des noms de fichiers lors d'un
160 commit :
162  $ git commit  # Tapez un message
163  $ find .git/objects -type f
165 Vous devriez voir maintenant trois objets. Mais là, je ne peux plus prédire le
166 nom des deux nouveaux fichiers puisqu'ils dépendent en partie du nom de fichier
167 que vous avez choisi. Nous continuerons en supposant que vous avez choisi
168 ``rose''. Si ce n'est pas le cas, vous pouvez réécrire l'histoire pour que ce
169 soit le cas :
171  $ git filter-branch --tree-filter 'mv VOTRE_NOM_DE_FICHIER rose'
172  $ find .git/objects -type f
174 Le fichier +.git/objects/9a/6a950c3b14eb1a3fb540a2749514a1cb81e206+ devrait
175 maintenant apparaître puisque c'est l'empreinte SHA1 du contenu suivant :
177  "tree" SP "32" NUL "100644 rose" NUL 0x9a6a950c3b14eb1a3fb540a2749514a1cb81e206
179 Vérifiez que ce contenu est le bon en tapant :
181  $ echo 9a6a950c3b14eb1a3fb540a2749514a1cb81e206 | git cat-file --batch
183 Avec zpipe, il est plus simple de vérifier l'empreinte :
185  $ zpipe -d < .git/objects/9a/6a950c3b14eb1a3fb540a2749514a1cb81e206 | sha1sum
187 La vérification de l'empreinte est plus difficile via cat-file puisque cette
188 commande n'affiche pas que le contenu brut du fichier après décompression.
190 Cette fichier est un objet arbre ('tree') : une liste de tuples constitués d'un
191 type, d'un nom de fichier et d'une empreinte. Dans notre exemple, le type est
192 100644 qui indique que `rose` est un fichier normal et l'empreinte est celle de
193 l'objet de type blob contenant le contenu de `rose`. Les autres types possibles
194 pour un fichier sont exécutable, lien symbolique ou dossier. Dans ce dernier
195 cas, l'empreinte représente un autre objet de type arbre.
197 Si vous faites appel à la commande filter-branch, vous verrez apparaître de
198 vieux objets dont vous n'avez pas besoin. Même s'ils disparaîtront
199 automatiquement une fois expirée la période de rétention, nous allons les
200 effacer dès maintenant pour rendre notre petit exemple plus facile à suivre :
202  $ rm -r .git/refs/original
203  $ git reflog expire --expire=now --all
204  $ git prune
206 Sur de vrais projets, vous devriez éviter de telles commandes puisqu'elles
207 détruisent les sauvegardes. Si vous voulez un dossier propre, il est
208 conseillé de faire un tout nouveau clone. Faites aussi attention si vous
209 manipulez directement le contenu de +.git+&#160;: que se passera-t-il si une
210 commande Git s'effectue au même moment ou si le courant est soudainement
211 coupé ?
213 De manière générale, les refs devraient toujours être effacées via *git
214 update-ref -d* même si on considère comme sans risque la suppression manuelle
215 de +refs/original+.
217 === Les commits ===
219 Nous avons expliqué 2 des 3 types d'objets. Le troisième est l'objet
220 'commit'. Son contenu dépend du message de commit ainsi que de la date et
221 l'heure auxquelles il a été créé. Pour que vous obteniez la même chose qu'ici,
222 nous devons bidouiller un peu :
224  $ git commit --amend -m Shakespeare  # Changement de message de commit
225  $ git filter-branch --env-filter 'export
226      GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800"
227      GIT_AUTHOR_NAME="Alice"
228      GIT_AUTHOR_EMAIL="alice@example.com"
229      GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
230      GIT_COMMITTER_NAME="Bob"
231      GIT_COMMITTER_EMAIL="bob@example.com"'  # Trucage de la date, l'heure et l'auteur.
232  $ find .git/objects -type f
234 Le fichier +.git/objects/ae/9d1241b2b6eea90529149a065f6bc444365c2a+ devrait
235 maintenant exister puisque c'est l'empreinte SHA1 du contenu suivant :
237  "commit 158" NUL
238  "tree 9a6a950c3b14eb1a3fb540a2749514a1cb81e206" LF
239  "author Alice <alice@example.com> 1234567890 -0800" LF
240  "committer Bob <bob@example.com> 1234567890 -0800" LF
241  LF
242  "Shakespeare" LF
244 Comme précédemment, vous pouvez utiliser zpipe ou cat-file pour vérifier par
245 vous-même.
247 C'est le premier commit, ce qui explique pourquoi il n'y a pas de commit
248 parent. Mais les commits suivants contiendront toujours au moins une ligne
249 identifiant un commit parent.
251 === Indiscernable de la magie ===
253 Les secrets de Git semblent trop simples. On imagine qu'il suffit de mélanger
254 quelques scripts shell et d'y ajouter une pincée de code C pour mitonner un tel
255 système en quelques heures : un assemblage d'opérations basiques sur les
256 fichiers et de calcul d'empreintes SHA1 garni de quelques fichiers verrou et
257 d'appels à fsync pour la robustesse. En fait, nous venons précisément de
258 décrire les premières versions de Git. Malgré tout, mis à part quelques
259 techniques astucieuses de compression pour gagner de la place et d'indexation
260 pour gagner du temps, nous savons maintenant comment Git transforme adroitement
261 un système de fichiers en une base de données parfaitement adaptée à de la
262 gestion de versions.
264 Par exemple, si un fichier quelconque de la base d'objets vient à être corrompu
265 par une erreur disque alors son empreinte ne correspond plus et nous sommes
266 alertés du problème. En calculant l'empreinte des empreintes d'autres objets,
267 nous maintenons l'intégrité à tous les niveaux. Les commits sont atomiques
268 puisque ils ne peuvent jamais mémoriser des modifications partiellement
269 stockées : nous ne pouvons calculer l'empreinte d'un commit et le stocker dans
270 la base d'objets qu'après y avoir déjà stocké tous les arbres, blobs et parents
271 relatifs à ce commit. La base d'objets est immunisée contre les interruptions
272 inattendues telles que les coupures de courant.
274 Nous faisons même échouer les tentatives d'attaque les plus sournoises.
275 Supposez que quelqu'un tente de modifier discrètement le contenu d'un fichier
276 dans l'une des anciennes versions du projet. Pour rendre cohérent le contenu de
277 la base d'objets, il lui faut changer l'empreinte de l'objet blob correspondant
278 puisque elle doit maintenant représenter une chaîne d'octets différente. Cela
279 signifie qu'il doit aussi changer l'empreinte de tous les arbres référençant ce
280 blob et donc changer l'empreinte de tous les commits impliquant ces arbres
281 ainsi que de tous les descendants de ces commits. Cela implique que l'empreinte
282 du HEAD officiel diffère de celle du HEAD d'un dépôt corrompu. En remontant la
283 suite d'empreintes erronées nous pouvons localiser avec précision le fichier
284 corrompu ainsi que le premier commit où il l'a été.
286 En résumé, tant que nous sommes sûrs des 20 octets représentant le dernier
287 commit, il est impossible d'altérer un dépôt Git.
289 Qu'en est-il des fameuses fonctionnalités de Git ? Des branchements ? Des
290 fusions ? Des tags ? De simples détails. La tête courante est conservée dans le
291 fichier +.git/HEAD+ qui contient l'empreinte d'un objet commit. Cette empreinte
292 sera tenue à jour durant un commit ainsi que durant de nombreuses autres
293 commandes. Les branches fonctionnent de manière similaire : ce sont des
294 fichiers dans +.git/refs/heads+. Et les tags aussi : ils sont dans
295 +.git/refs/tags+ mais ils sont mis à jour par un ensemble différent de
296 commandes.
298 // LocalWords:  doc flyspell coding utf fill-column Git référez-vous commits mv
299 // LocalWords:  git cryptographiques SHA chains d'horodatage rm add
300 // LocalWords:  stat List Linux object database tags logs HEAD blobs trees echo
301 // LocalWords:  init find objects puis-je blob SP LF cb eae printf sha sum zlib
302 // LocalWords:  demandez-vous Passez-les zpipe cat-file filter-branch tree eb
303 // LocalWords:  tree-filter fb batch tuples reflog now all refs update-ref Fri
304 // LocalWords:  amend Shakespeare env-filter export AUTHOR Feb NAME Alice EMAIL
305 // LocalWords:  COMMITTER author committer shell fsync