Git
Chapters ▾ 2nd Edition

10.6 Git’in Daxili İşləri - Transfer Protokolları

Transfer Protokolları

Git iki depo arasında məlumatları iki əsas şəkildə ötürə bilər: “dumb' protokolu və ``smart” protokolu. Bu bölmə bu iki əsas protokolun necə işlədiyini tez bir zamanda əhatə edəcəkdir.

The Dumb Protokolu

Yalnız bir oxunuşda HTTP üzərindən təqdim ediləcək bir depo qurursanız, dumb protokolundan istifadə edilməsi ehtimalı böyükdür. Bu protokol “dumb” adlanır, çünki nəqliyyat prosesi zamanı server tərəfində Git-ə məxsus bir kod tələb olunmur; gətirmə prosesi, müştərinin serverdəki Git deposunun tərtibatını qəbul edə biləcəyi bir sıra HTTP GET istəkləridir.

Note

Bu günlərdə dumb protokolu kifayət qədər nadir hallarda istifadə olunur. Təhlükəsizliyi təmin etmək və ya özəlləşdirmək çətindir, buna görə də Git host-larının əksəriyyəti (həm cloud əsaslı, həm də on-premises) istifadə etməkdən imtina edəcəkdir. Ümumiyyətlə bir azdan izah etdiyimiz daha ağıllı protokoldan istifadə etməyiniz tövsiyə olunur.

Simplegit kitabxanası üçün http-fetch prosesini izləyək:

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

Bu əmrin ilk işi info/refs faylını pull etməkdir. Bu fayl update-server-info əmri ilə yazılmışdır, bu səbəbdən HTTP nəqlinin düzgün işləməsi üçün bunu post-receive hook olaraq təmin etməlisiniz:

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

Artıq remote istinadların və SHA-1-lərin siyahısı var. Daha sonra, HEAD istinadının nə olduğunu axtarırsınız, belə ki bitirdikdən sonra nəyi yoxlayacağınızı biləcəksiniz:

=> GET HEAD
ref: refs/heads/master

Prosesi tamamladıqdan sonra master branch-nı yoxlamalısınız. Bu anda gəzinti prosesinə başlamağa hazırsınız. Başlanğıc nöqtəniz info/refs faylında gördüyünüz ca82a6 commit obyekti olduğundan, bunu əldə etməyə başlayırsınız:

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

Bir obyekt geri alırsınız – hanı ki, bu obyekt serverdə boş formatda olur və onu statik bir HTTP GET istəyi üzərinə götürmüşdünüz. Siz onu zlib-uncompress edə bilər, başlığı soyub, məzmuna baxa bilərsiniz:

$ 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

Sonra, sizin alacağınız daha iki obyektiniz var - yeni aldığımız commit-in göstərdiyi məzmun ağacından olan cfda3b və valideyn olan 085bb3:

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

O sizə bir sonrakı obyekt commit-ini verir. Ağac obyektini götürün:

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

Oy! – belə görünür ki bu ağac obyekti serverdə boş formatda deyil, buna görə 404 cavabını geri alırsınız. Bunun bir neçə səbəbi ola bilər - obyekt alternativ bir depoda ola bilər və ya bu depodakı packfile-da da ola bilər. Git əvvəlcə siyahıda göstərilən alternativləri yoxlayır:

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

Əgər o, alternativ URL-lərin siyahısı ilə geri qayıdırsa, Git boş faylları və packfile-ları yoxlayır - bu, bir-birinə çəngəllənən layihələrin diskdəki obyektləri paylaşması üçün gözəl bir mexanizmdir. Bununla birlikdə, bu vəziyyətdə alternativlər göstərilmədiyindən, obyektiniz packfile-da olmalıdır. Bu serverdə hansı packfile-ların mövcud olduğunu görmək üçün bunların siyahısını ehtiva edən (update-server-info tərəfindən yaradılan) objects/info/packs faylını əldə etməlisiniz.

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

Serverdə yalnız bir packfile var, buna görə obyektiniz açıq-aydın şəkildə oradadır, ancaq əmin olmaq üçün indeks faylını yoxlayacaqsınız. Serverdə birdən çox packfile-nız varsa, bu da sizə lazım olan obyekti hansı packfile-da görə biləcəyiniz üçün faydalıdır:

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

Artıq packfile indeksinə sahib olduğunuzdan, obyektinizin içində olub olmadığını görə bilərsiniz - çünki indeks packfile-nızdakı obyektlərin SHA-1-lərini və həmin obyektlərin əvəzlərini siyahıya alır. Hədəfiniz oradadır, davam edin və bütün packfile-ı əldə edin:

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

Indi sizin ağac obyektiniz mövcuddur və siz commit-lərinizi yeritməyə davam edə bilərsiniz. Onların hamısı həm də hal-hazırda yüklədiyiniz packfile-da olduğundan serverə başqa request-lər göndərməyinizə ehtiyac yoxdur. Git əvvəlində yüklədiyiniz HEAD arayışı ilə işarə edilən master branch-ın işləyən bir nüsxəsini yoxlayır.

The Smart Protokolu

Dumb protokolu sadə, lakin bir az səmərəsizdir və müştəridən serverə məlumat yazmaqla işləyə bilmir. Smart protokolu daha çox məlumat ötürmə metodudur, lakin Git haqqında ağıllı bir proses tələb edir – o, yerli məlumatları oxuya bilir, müştərinin nəyə ehtiyac duyduğunu tapır və bunun üçün xüsusi bir paket yarada bilir. Məlumatların ötürülməsi üçün iki proses dəsti mövcuddur: uploading data üçün bir cüt və downloading data üçün bir cüt.

Uploading Data

Verilənləri remote prosesə yükləmək üçün Git, send-packreceive-pack proseslərindən istifadə edir. send-pack prosesi müştəri üzərində işləyir və remote-dakı receive-pack prosesinə qoşulur.

SSH

Məsələn, proyektinizdə git push origin master işlətdiyinizi düşünək və origin SSH protokolundan istifadə edən bir URL olaraq təyin olunduğunu deyək. Git, SSH üzərindən serverinizə bir əlaqə quran send-pack prosesini işə salır. O, remote serverdə belə bir şeyə bənzəyən bir SSH çağırışı vasitəsilə bir əmr işlətməyə çalışır:

$ 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

git-receive-pack əmri, hazırda sahib olduğu hər bir istinad üçün dərhal bir sətirlə cavab verir – yəni, bu halda yalnız master branch-ı və onun SHA-1-i. Birinci sətirdə serverin imkanlarının siyahısı (burada, report-status, delete-refs və digərləri, o cümlədən müştəri identifikatoru) var.

Məlumatlar chunk-lara ötürülür. Hər bir chunk, onun nə qədər olduğunu göstərən 4 simvollu bir hex dəyəri ilə başlayır (uzunluğun özünün 4 baytı da daxil olmaqla). Chunk-lar adətən tək bir məlumat sətri və arxadakı bir xətti qidalandırır. İlk chunk-nız 165 hexadecimal üçün 00a5 ilə başlayır, yəni chunk 165 bayt uzunluğundadır. Növbəti chunk 0000-dir, yəni server istinadlar siyahısı ilə hazırlanır.

Artıq serverin vəziyyətini bildiyindən, send-pack prosesi, serverin etmədiyi commit-i müəyyənləşdirir. Bu push-un yeniləyəcəyi hər istinad üçün, send-pack prosesi bu məlumatı receive-pack prosesinə izah edir. Məsələn, master branch-ı yeniləyirsinizsə və bir experiment branch-na əlavə edirsinizsə, send-pack cavabı belə görünə bilər:

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

Git, xəttin uzunluğu, köhnə SHA-1, yeni SHA-1 və yenilənən istinadla yenilədiyiniz hər bir istinad üçün bir sətir göndərir. Birinci sətir də müştərinin imkanlarına malikdir. Bütün 0’ların SHA-1 dəyəri əvvəllər heç bir şey olmadığı demək deyil - çünki təcrübə istinadını siz əlavə edirsiniz. Əgər bir arayışı silsəniz, əksini görəcəksiniz: bütün '0-lar sağ tərəfdədir.

Sonra müştəri serverdə hələ mövcud olmayan bütün obyektlərin paketini göndərir. Nəhayət, server müvəffəqiyyət (və ya uğursuzluq) göstəricisi ilə cavab verir:

000eunpack ok
HTTP(S)

Bu proses əsasən HTTP-də də eynidir, baxmayaraq ki, qarşılıqlı əlaqə biraz fərqlidir. Əlaqə bu istəklə başladı:

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

İlk müştəri-server mübadiləsi başa çatdı. Müştəri daha sonra başqa bir sorğu göndərir, bu dəfə send-pack-nin verdiyi məlumatlarla POST göndərilir.

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

POST sorğusuna, send-pack output-u və packfile faydalı yük kimi daxildir. Server daha sonra HTTP cavabı ilə müvəffəq və ya uğursuz olduğunu göstərir.

HTTP protokolunun bu məlumatları yığılmış köçürmə kodlaşdırmasının içərisinə əlavə edə biləcəyini unutmayın.

Downloading Data

Verilənləri yüklədiyiniz zaman, fetch-packupload-pack prosesləri iştirak edir. Müştəri hansı məlumatların ötürüləcəyini müzakirə etmək üçün remote-dakı bir upload-pack prosesinə qoşulan bir fetch-pack prosesinə başlayır.

SSH

SSH üzərindən gətirmə edirsinizsə, fetch-pack belə bir şey işlədir:

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

fetch-pack qoşulduqdan sonra, upload-pack geriyə belə bir şey göndərir:

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

Bu, receive-pack cavabları ilə çox oxşardır, lakin imkanları fərqlidir. Bundan əlavə, HEAD-in işarə etdiyini geri göndərir (symref=HEAD:refs/heads/master), buna görə müştəri bunun bir klon olub olmadığını yoxlayacağını bilir.

Bu nöqtədə, fetch-pack prosesi hansı obyektlərə sahib olduğuna baxır və ehtiyac duyduğu obyektlərə “want” və sonra SHA-1 göndərərək cavab verir. Artıq sahib olduğu bütün obyektləri “have” və sonra SHA-1 ilə göndərir. Bu siyahının sonunda, ehtiyac duyduğu məlumatların paket sənədini göndərməyə başlamaq üçün upload-pack prosesini başlamaq üçün “done” yazır:

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

Fetch əməliyyatı üçün əl sıxma iki HTTP request-i alır. Birincisi, dumb protokolda eyni nöqtədə istifadə olunan bir GET-dir:

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

Bu, SSH bağlantısı üzərindən git-upload-pack çağırmağa çox oxşayır, lakin ikinci mübadilə ayrı bir istək olaraq həyata keçirilir:

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

Yenə də, bu yuxarıdakı ilə eyni formatdadır. Bu sorğunun cavabı müvəffəq və ya uğursuz olduğunu göstərir və paket sənədini də əhatə edir.

Protokolun Nəticəsi

Bu bölmə köçürmə protokollarının çox əsas bir icmalını ehtiva edir. Protokolda multi_ack və ya side-band imkanları kimi bir çox digər xüsusiyyətlər var, lakin bunları əhatə etmək bu kitabın əhatə dairəsindən xaricdədir. Sizə müştəri və server arasında ümumi geri və irəli bir fikir verməyə çalışdıq; bundan daha çox biliyə ehtiyacınız varsa, ehtimal ki, Git mənbə koduna nəzər yetirmək istərdiniz.