Git
Chapters ▾ 2nd Edition

10.6 Notranjost Gita - Protokoli prenosa

Protokoli prenosa

Git lahko prenaša podatke med dvema repozitorijema na dva glavna načina: »neumni« (angl. dumb) in »pametni« (angl. smart) protokol. V tem razdelku bomo hitro predstavili, kako ta dva glavna protokola delujeta.

Neumni protokol

Če nastavljate repozitorij za strežbo samo za branje prek protokola HTTP, je verjetno, da bo uporabljen »neumni« protokol. Ta protokol se imenuje »neumen«, ker med prenosnim postopkom na strežniški strani ne zahteva nobene Git-specifične kode; postopek pridobivanja je sestavljen iz serije zahtev HTTP GET, pri čemer lahko odjemalec predpostavi postavitev repozitorija Git na strežniku.

Opomba

Neumni protokol se danes precej redko uporablja. Težko ga je zaščititi ali ohraniti zasebnega, zato ga večina gostiteljev Git (tako tisti na osnovi oblaka kot tisti na osnovi uporabe) zavrača. Navadno je priporočljivo uporabljati pametni protokol, o katerem bomo pisali več kasneje.

Sledimo postopku http-fetch za knjižnico simplegit:

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

Prva stvar, ki jo ta ukaz naredi, je, da prenese datoteko info/refs. To datoteko napiše ukaz update-server-info, zato ga morate omogočiti kot kljuko post-receive, da prenos HTTP pravilno deluje:

=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949     refs/heads/master

Zdaj imate seznam oddaljenih referenc in SHA-1. Nato poiščete, katera referenca je HEAD, da veste, kaj izvleči, ko končate:

=> GET HEAD
ref: refs/heads/master

Ko ste končali postopek, morate izvleči vejo master. Sedaj ste pripravljeni na začetek sprehoda. Ker je vaša začetna točka objekt s potrditvijo ca82a6, ki ste jo videli v datoteki info/refs, začnete s pridobitvijo tega objekta:

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

Nazaj ste dobili objekt — ta objekt je na strežniku v šibki obliki, in pridobili ste ga z običajnim zahtevkom HTTP GET. Objekt lahko razpakirate z zlib, odstranite glavo in si ogledate vsebino potrditve:

$ 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

Change version number

Naslednje, imate dva dodatna objekta za pridobitev — cfda3b, ki je drevo vsebine, na katero kaže pravkar pridobljena potrditev; in 085bb3, ki je predhodna potrditev:

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

To vam da naslednji objekt potrditve. Zagrabite objekt drevesa:

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

Ups — zdi se, da tega objekta drevesa ni na strežniku v šibki obliki, zato dobite odgovor 404. Za to obstaja nekaj razlogov — objekt bi lahko bil v alternativnem repozitoriju, ali pa bi bil v tem repozitoriju v pakirani obliki. Git najprej preveri, ali obstajajo navedene alternative:

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

Če se vrne seznam alternativnih URL-jev, Git preveri, ali tam obstajajo šibke datoteke in paketne datoteke — to je lep mehanizem za projekte, ki so razvejitve eden od drugega, da si delijo objekte na disku. Vendar pa, ker v tem primeru ni našteta nobena alternativa, mora biti vaš objekt v paketni datoteki. Da vidite, katere paketne datoteke so na voljo na tem strežniku, morate pridobiti datoteko objects/info/packs, ki vsebuje seznam (tudi generiran z update-server-info):

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

Na strežniku je samo en paket datotek, zato je vaš objekt očitno tam, vendar preverili boste vseeno indeksno datoteko, da boste prepričani. To je tudi uporabno, če imate na strežniku več paketnih datotek, tako da lahko vidite, katera paketna datoteka vsebuje objekt, ki ga potrebujete:

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

Zdaj, ko imate indeks paketne datoteke, lahko preverite, ali je vaš objekt v njej — ker indeks izpiše seznam SHA-1 objektov, ki so vsebovani v paketni datoteki, in njihove odmike. Vaš objekt je tam, zato nadaljujte in dobite celotno paketno datoteko:

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

Imate svoj objekt drevesa, zato nadaljujete hojo po svojih potrditvah. Vse potrditve so prav tako v paketni datoteki, ki ste jo pravkar prenesli, zato vam ni treba več pošiljati zahtevkov na strežnik. Git izvleče delovno kopijo veje master, na katero kaže sklic HEAD, ki ste ga prenesli na začetku.

Pametni protokol

Neumni protokol je preprost, vendar malo neučinkovit in ne more obravnavati pisanja podatkov s strani odjemalca na strežnik. Pametni protokol je bolj pogosta metoda prenosa podatkov, vendar zahteva proces na oddaljenem koncu, ki je inteligenten glede Gita — lahko bere lokalne podatke, ugotovi, kaj ima in kaj potrebuje odjemalec in za to generira prilagojeno paketno datoteko. Obstajata dve vrsti procesov za prenos podatkov: par za nalaganje podatkov in par za prejemanje podatkov.

Nalaganje podatkov

Za nalaganje podatkov na oddaljeni proces Git uporablja procesa send-pack in receive-pack. Proces send-pack se izvaja na odjemalcu in se poveže s procesom receive-pack na oddaljeni strani.

SSH

Na primer, recimo, da v vašem projektu zaženete git push origin master in je origin definiran kot URL, ki uporablja protokol SSH. Git bo zagnal proces send-pack, ki sproži povezavo prek SSH na vaš strežnik. Na oddaljenem strežniku poskuša zagnati ukaz prek klica SSH, ki je videti nekako takole:

$ 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

Ukaz git-receive-pack takoj odgovori z eno vrstico za vsako referenco, ki jo trenutno ima — v tem primeru le vejo master in njen SHA-1. Prva vrstica ima tudi seznam zmogljivosti strežnika (tukaj report-status, delete-refs in nekaj drugih, vključno z identifikatorjem odjemalca).

Podatki se prenašajo v kosih. Vsak kos se začne s 4-znakovno šestnajstiško vrednostjo, ki določa, kako dolg je kos (vključno s 4 bajti dolžine). Koščki običajno vsebujejo eno vrstico podatkov in zaključno novo vrstico. Prvi kos se začne z 00a5, kar je šestnajstiško število za 165, kar pomeni, da je kos dolg 165 bajtov. Naslednji kos je 0000, kar pomeni, da je strežnik končal z naštevanjem referenc.

Ko pozna strežnikovo stanje, proces send-pack določi, katere potrditve ima odjemalec, strežnik pa ne. Za vsako vejo, ki jo bo ta potisk posodobil, proces send-pack pove procesu receive-pack te informacije. Na primer, če posodabljate vejo master in dodajate vejo experiment, bo odgovor send-pack nekaj podobnega temu:

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

Git pošlje vrstico za vsako referenco, ki jo posodabljate, s podatki o dolžini vrstice, starem SHA-1, novem SHA-1 in referenci, ki jo posodabljate. Prva vrstica ima tudi zmogljivosti odjemalca. Vrednost SHA-1, ki je sestavljena iz samih 0, pomeni, da ničesar ni bilo tam prej — ker dodajate referenco experiment. Če bi brisali referenco, bi videli nasprotno: same 0 na desni strani.

Nato odjemalec pošlje paket z vsemi objekti, ki jih strežnik še nima. Na koncu strežnik odgovori z indikacijo uspeha (ali neuspeha):

000eunpack ok
HTTP(S)

Ta postopek je večinoma enak prek protokola HTTP, čeprav je rokovanje (angl. handshaking) nekoliko drugačno. Povezava se začne s tem zahtevkom:

=> GET http://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

To je konec prve izmenjave med odjemalcem in strežnikom. Nato odjemalec pošlje še en zahtevek, tokrat POST, s podatki, ki jih ponuja send-pack.

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

Zahtevek POST vključuje izhod send-pack in paketno datoteko kot svoj tovor. Strežnik nato s svojim odzivom HTTP označi uspeh ali neuspeh.

Upoštevajte, da lahko protokol HTTP te podatke dodatno ovije v kosovno prenosno kodiranje.

Prenos podatkov

Ko prenašate podatke, so vključeni procesi fetch-pack in upload-pack. Naročnik sproži postopek fetch-pack, ki se poveže s postopkom upload-pack na oddaljeni strani, da se dogovorita, kateri podatki se bodo prenesli navzdol.

SSH

Če izvajate pridobivanje prek SSH, fetch-pack deluje nekako takole:

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

Ko se fetch-pack poveže, upload-pack pošlje nekaj podobnega temu:

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
003fe2409a098dc3e53539a9028a94b6224db9d6a6b6 refs/heads/master
0000

To je zelo podobno temu, s čimer receive-pack odgovarja, vendar so zmožnosti drugačne. Poleg tega pošlje nazaj, kam kaže HEAD (symref=HEAD:refs/heads/master), tako da ve, kaj naj preveri, ali gre za klon.

V tem trenutku proces fetch-pack pogleda, katere objekte ima, in odgovori z objekti, ki jih potrebuje, tako da pošlje »want« in nato SHA-1, ki ga želi. Pošlje vse objekte, ki jih že ima, s »have« in nato SHA-1. Na koncu seznama zapiše »done«, da sproži proces upload-pack, ki začne pošiljati paketno datoteko podatkov, ki jih potrebuje:

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

Rokovanje za pridobivanje podatkov zahteva dva zahtevka HTTP. Prvi je GET na enako končno točko, ki se uporablja v neumnem protokolu:

=> 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

To je zelo podobno klicanju git-upload-pack prek povezave SSH, vendar se druga izmenjava izvede kot ločen zahtevek:

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

Tudi to je enak format kot zgoraj. Odziv na to zahtevo označuje uspeh ali neuspeh ter vključuje paketno datoteko.

Povzetek protokolov

Ta razdelek vsebuje zelo osnovni pregled prenosnih protokolov. Protokol vključuje veliko drugih funkcij, kot sta sposobnosti multi_ack ali side-band, vendar njihovo obdelovanje presega obseg te knjige. Poskušali smo vam dati občutek o splošnem dvosmernem pretoku med odjemalcem in strežnikom; če potrebujete več znanja kot to, boste verjetno želeli pogledati izvorno kodo Gita.

scroll-to-top