Git 🌙
Chapters â–Ÿ 2nd Edition

10.6 Les tripes de Git - Les protocoles de transfert

Les protocoles de transfert

Git peut transfĂ©rer des donnĂ©es entre deux dĂ©pĂŽts de deux façons principales : le protocole « stupide » et le protocole « intelligent ». Cette section fait un tour d’horizon du fonctionnement de ces deux protocoles.

Le protocole stupide

Si vous mettez en place un dĂ©pĂŽt Ă  accĂ©der en lecture seule sur HTTP, c’est vraisemblablement le protocole stupide qui sera utilisĂ©. Ce protocole est dit « stupide », car il ne nĂ©cessite aucun code spĂ©cifique Ă  Git cĂŽtĂ© serveur durant le transfert ; le processus de rĂ©cupĂ©ration est une sĂ©rie de requĂȘtes GET, oĂč le client devine la structure du dĂ©pĂŽt Git prĂ©sent sur le serveur.

Note

Le protocole stupide est rarement utilisĂ© ces derniers temps. Il est difficile de le rendre sĂ©curisĂ© ou privĂ©, et donc la plupart des hĂ©bergeurs Git (sur le cloud ou sur serveur dĂ©diĂ©) refusent de l’utiliser. On conseille gĂ©nĂ©ralement d’utiliser le protocole intelligent, qui est dĂ©crit plus loin.

Suivons le processus http-fetch pour la bibliothÚque simplegit :

$ git clone https://server/simplegit-progit.git

La premiĂšre chose que fait cette commande est de rĂ©cupĂ©rer le fichier info/refs. Ce fichier est Ă©crit par la commande update-server-info et c’est pour cela qu’il faut activer le crochet post-receive, sinon le transfert HTTP ne fonctionnera pas correctement :

> GET info/refs
ca82a6dff817ec66f44342007202690a93763949     refs/heads/master

On possÚde maintenant une liste des références distantes et empreintes SHA-1. Ensuite, on regarde vers quoi pointe HEAD, pour savoir sur quelle branche se placer quand on aura fini :

> GET HEAD
ref: refs/heads/master

On aura besoin de se placer sur la branche master, quand le processus sera terminĂ©. On est maintenant prĂȘt Ă  dĂ©marrer le processus de parcours. Puisque votre point de dĂ©part est l’objet commit ca82a6 que vous avez vu dans le fichier info/refs, vous commencez par le rĂ©cupĂ©rer :

> GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)

Vous obtenez un objet, cet objet est dans le format brut sur le serveur et vous l’avez rĂ©cupĂ©rĂ© Ă  travers une requĂȘte HTTP GET statique. Vous pouvez le dĂ©compresser avec zlib, ignorer l’en-tĂȘte et regarder le contenu du commit :

$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <schacon@gmail.com> 1205815931 -0700
committer Scott Chacon <schacon@gmail.com> 1240030591 -0700

changed the version number

Puis, vous avez deux autres objets supplĂ©mentaires Ă  rĂ©cupĂ©rer : cfda3b qui est l’arbre du contenu sur lequel pointe le commit que nous venons de rĂ©cupĂ©rer et 085bb3 qui est le commit parent :

> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)

Cela vous donne le prochain objet commit. RĂ©cupĂ©rez l’objet arbre :

> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)

Oups, on dirait que l’objet arbre n’est pas au format brut sur le serveur, vous obtenez donc une rĂ©ponse 404. On peut en dĂ©duire certaines raisons : l’objet peut ĂȘtre dans un dĂ©pĂŽt supplĂ©ant ou il peut ĂȘtre dans un fichier groupĂ© de ce dĂ©pĂŽt. Git vĂ©rifie la liste des dĂ©pĂŽts supplĂ©ants d’abord :

> GET objects/info/http-alternates
(empty file)

Si la rĂ©ponse contenait une liste d’URL supplĂ©antes, Git aurait cherchĂ© les fichiers bruts et les fichiers groupĂ©s Ă  ces emplacements, c’est un mĂ©canisme sympathique pour les projets qui ont dĂ©rivĂ© d’un autre pour partager les objets sur le disque. Cependant, puisqu’il n’y a pas de supplĂ©ants listĂ©s dans ce cas, votre objet doit se trouver dans un fichier groupĂ©. Pour voir quels fichiers groupĂ©s sont disponibles sur le serveur, vous avez besoin de rĂ©cupĂ©rer le fichier objects/info/packs, qui en contient la liste (gĂ©nĂ©rĂ©e Ă©galement par update-server-info) :

> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack

Il n’existe qu’un seul fichier groupĂ© sur le serveur, votre objet se trouve Ă©videmment dedans, mais vous allez tout de mĂȘme vĂ©rifier l’index pour ĂȘtre sĂ»r. C’est Ă©galement utile lorsque vous avez plusieurs fichiers groupĂ©s sur le serveur, vous pouvez donc voir quel fichier groupĂ© contient l’objet dont vous avez besoin :

> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)

Maintenant que vous avez l’index du fichier groupĂ©, vous pouvez vĂ©rifier si votre objet est bien dedans car l’index liste les empreintes SHA-1 des objets contenus dans ce fichier groupĂ© et des emplacements de ces objets. Votre objet est lĂ , allez donc rĂ©cupĂ©rer le fichier groupĂ© complet :

> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)

Vous avez votre objet arbre, vous continuez donc le chemin des commits. Ils sont Ă©galement tous contenus dans votre fichier groupĂ© que vous venez de tĂ©lĂ©charger, vous n’avez donc pas d’autres requĂȘtes Ă  faire au serveur. Git rĂ©cupĂšre une copie de travail de votre branche master qui Ă©tĂ© rĂ©fĂ©rencĂ©e par HEAD que vous avez tĂ©lĂ©chargĂ© au dĂ©but.

Le protocole intelligent

Le protocole stupide est simple mais un peu inefficace, et il ne permet pas l’écriture de donnĂ©es du client au serveur. Le protocole intelligent est une mĂ©thode plus habituelle pour transfĂ©rer des donnĂ©es, mais elle nĂ©cessite l’exĂ©cution sur le serveur d’un processus qui connaĂźt Git : il peut lire les donnĂ©es locales et dĂ©terminer ce que le client a ou ce dont il a besoin pour gĂ©nĂ©rer un fichier groupĂ© personnalisĂ© pour lui. Il y a deux ensembles d’exĂ©cutables pour transfĂ©rer les donnĂ©es : une paire pour tĂ©lĂ©verser des donnĂ©es et une paire pour en tĂ©lĂ©charger.

Téléverser des données

Pour tĂ©lĂ©verser des donnĂ©es vers un exĂ©cutable distant, Git utilise les exĂ©cutables send-pack et receive-pack. L’exĂ©cutable send-pack tourne sur le client et se connecte Ă  l’exĂ©cutable receive-pack du cĂŽtĂ© serveur.

======= SSH

Par exemple, disons que vous exĂ©cutez git push origin master dans votre projet et origin est dĂ©fini comme une URL qui utilise le protocole SSH. Git appelle l’exĂ©cutable send-pack, qui initialise une connexion Ă  travers SSH vers votre serveur. Il essaye d’exĂ©cuter une commande sur le serveur distant via un appel SSH qui ressemble à :

$ ssh -x git@server "git-receive-pack 'simplegit-progit.git'"
00a5ca82a6dff817ec66f4437202690a93763949 refs/heads/master report-status \
	delete-refs side-band-64k quiet ofs-delta \
	agent=git/2:2.1.1+github-607-gfba4028 delete-refs
0000

La commande git-receive-pack rĂ©pond immĂ©diatement avec une ligne pour chaque rĂ©fĂ©rence qu’elle connaĂźt actuellement, dans ce cas, uniquement la branche master et son empreinte SHA-1. La premiĂšre ligne contient Ă©galement une liste des compĂ©tences du serveur (ici : report-status, delete-refs et quelques autres, dont l’identifiant du client).

Chaque ligne commence avec une valeur hexadĂ©cimale sur 4 caractĂšres, spĂ©cifiant le reste de la longueur de la ligne. La premiĂšre ligne, ici, commence avec 00a5, soit 165 en hexadĂ©cimal, ce qui signifie qu’il y a 165 octets restants sur cette ligne. La ligne d’aprĂšs est 0000, signifiant que le serveur a fini de lister ses rĂ©fĂ©rences.

Maintenant qu’il connait l’état du serveur, votre exĂ©cutable send-pack dĂ©termine quels commits il a de plus que le serveur. L’exĂ©cutable send-pack envoie alors Ă  l’exĂ©cutable receive-pack les informations concernant chaque rĂ©fĂ©rence que cette commande push va mettre Ă  jour. Par exemple, si vous mettez Ă  jour la branche master et ajoutez la branche experiment, la rĂ©ponse de send-pack ressemblera Ă  quelque chose comme :

0076ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 \
	refs/heads/master report-status
006c0000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d \
	refs/heads/experiment
0000

Git envoie une ligne pour chaque rĂ©fĂ©rence que l’on met Ă  jour avec l’ancien SHA-1, le nouveau SHA-1 et la rĂ©fĂ©rence en train d’ĂȘtre mise Ă  jour. La premiĂšre ligne contient Ă©galement les compĂ©tences du client. La valeur SHA-1 remplie de '0' signifie qu’il n’y avait rien Ă  cet endroit avant, car vous ĂȘtes en train d’ajouter la rĂ©fĂ©rence experiment. Si vous Ă©tiez en train de supprimer une rĂ©fĂ©rence, vous verriez l’opposé : que des '0' du cĂŽtĂ© droit.

Puis, le client tĂ©lĂ©verse un fichier groupĂ© de tous les objets que le serveur n’a pas encore. Finalement, le serveur rĂ©pond avec une indication de succĂšs (ou d’échec) :

000eunpack ok
HTTP(S)

Le processus est quasiment le mĂȘme avec HTTP, Ă  une diffĂ©rence prĂšs lors de l’établissement de la liaison (handshaking). La connection est amorcĂ©e avec cette requĂȘte :

> GET https://server/simplegit-progit.git/info/refs?service=git-receive-pack
001f# service=git-receive-pack
00ab6c5f0e45abd7832bf23074a333f739977c9e8188 refs/heads/master \
	report-status delete-refs side-band-64k quiet ofs-delta \
	agent=git/2:2.1.1~vmg-bitmaps-bugaloo-608-g116744e
0000

Ceci est la fin du premier Ă©change client-serveur. Le client fait alors une nouvelle requĂȘte, qui est cette fois un POST, avec les donnĂ©es fournies par git-upload-pack.

> POST https://server/simplegit-progit.git/git-receive/pack

La requĂȘte POST contient la sortie de send-pack et le fichier groupĂ©. Enfin, le serveur indique le succĂšs ou l’échec dans sa rĂ©ponse HTTP.

Gardez en tĂȘte que le protocole HTTP peut ensuite envelopper ces donnĂ©es dans un encodage de transfert par paquets.

Téléchargement des données

Lorsque vous téléchargez des données, les exécutables fetch-pack et upload-pack entrent en jeu. Le client démarre un processus fetch-pack qui se connecte à un processus upload-pack du cÎté serveur pour négocier les données qui seront téléchargées.

SSH

Si vous téléchargez par SSH, fetch-pack fait quelque chose comme ceci :

$ ssh -x git@server "git-upload-pack 'simplegit-progit.git'"

Une fois fetch-pack connecté, upload-pack lui répond quelque chose du style :

00dfca82a6dff817ec66f44342007202690a93763949 HEAD multi_ack thin-pack \
	side-band side-band-64k ofs-delta shallow no-progress include-tag \
	multi_ack_detailed symref=HEAD:refs/heads/master \
	agent=git/2:2.1.1+github-607-gfba4028
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
0000

Ceci est trĂšs proche de la rĂ©ponse de receive-pack mais les compĂ©tences sont diffĂ©rentes. En plus, il envoie ce qui est pointĂ© par HEAD (symref=HEAD:refs/heads/master), afin que le client sache ce qu’il doit rĂ©cupĂ©rer dans le cas d’un clone.

À ce moment, fetch-pack regarde les objets qu’il a et rĂ©pond avec la liste des objets dont il a besoin en envoyant « want » (vouloir) suivi du SHA-1 qu’il veut. Il envoie tous les objets qu’il a dĂ©jĂ  avec « have » suivi du SHA-1. À la fin de la liste, il Ă©crit « done » (fait) pour inciter l’exĂ©cutable upload-pack Ă  commencer Ă  envoyer le fichier groupĂ© des donnĂ©es demandĂ©es :

003cwant ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009done
0000
HTTP(S)

L’établissement de la liaison pour une opĂ©ration de tĂ©lĂ©chargement nĂ©cessite deux requĂȘtes HTTP. La premiĂšre est un GET vers le mĂȘme point que dans le protocole stupide :

> GET $GIT_URL/info/refs?service=git-upload-pack
001e# service=git-upload-pack
00e7ca82a6dff817ec66f44342007202690a93763949 HEAD multi_ack thin-pack \
	side-band side-band-64k ofs-delta shallow no-progress include-tag \
	multi_ack_detailed no-done symref=HEAD:refs/heads/master \
	agent=git/2:2.1.1+github-607-gfba4028
003fca82a6dff817ec66f44342007202690a93763949 refs/heads/master
0000

Ceci ressemble beaucoup Ă  un appel Ă  git-upload-pack par une connection SSH, mais le deuxiĂšme Ă©change est fait dans une requĂȘte sĂ©parĂ©e :

> POST $GIT_URL/git-upload-pack HTTP/1.0
0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7
0032have 441b40d833fdfa93eb2908e52742248faf0ee993
0000

Une fois de plus, ce format est le mĂȘme que plus haut. La rĂ©ponse Ă  cette requĂȘte indique le succĂšs ou l’échec, et contient le fichier groupĂ©.

Résumé sur les protocoles

Cette section contient un survol basique des protocoles de transfert. Les protocoles contiennent de nombreuses autres fonctionalitĂ©s, comme les compĂ©tences multi_ack ou side-band, mais leur Ă©tude est hors du sujet de ce livre. Nous avons essayĂ© de vous donner une idĂ©e gĂ©nĂ©rale des Ă©changes entre client et serveur. Si vous souhaitez en connaĂźtre davantage, vous devrez probablement jeter un Ɠil sur le code source de Git.

scroll-to-top