-
1. Начало
- 1.1 За Version Control системите
- 1.2 Кратка история на Git
- 1.3 Какво е Git
- 1.4 Конзолата на Git
- 1.5 Инсталиране на Git
- 1.6 Първоначална настройка на Git
- 1.7 Помощна информация в Git
- 1.8 Обобщение
-
2. Основи на Git
-
3. Клонове в Git
-
4. GitHub
-
5. Git инструменти
- 5.1 Избор на къмити
- 5.2 Интерактивно индексиране
- 5.3 Stashing и Cleaning
- 5.4 Подписване на вашата работа
- 5.5 Търсене
- 5.6 Манипулация на историята
- 5.7 Мистерията на командата Reset
- 5.8 Сливане за напреднали
- 5.9 Rerere
- 5.10 Дебъгване с Git
- 5.11 Подмодули
- 5.12 Пакети в Git (Bundling)
- 5.13 Заместване
- 5.14 Credential Storage система
- 5.15 Обобщение
-
6. Настройване на Git
- 6.1 Git конфигурации
- 6.2 Git атрибути
- 6.3 Git Hooks
- 6.4 Примерна Git-Enforced политика
- 6.5 Обобщение
-
7. Git и други системи
- 7.1 Git като клиент
- 7.2 Миграция към Git
- 7.3 Обобщение
-
8. Git на ниско ниво
- 8.1 Plumbing и Porcelain команди
- 8.2 Git обекти
- 8.3 Git референции
- 8.4 Packfiles
- 8.5 Refspec спецификации
- 8.6 Транспортни протоколи
- 8.7 Поддръжка и възстановяване на данни
- 8.8 Environment променливи
- 8.9 Обобщение
-
9. Приложение A: Git в други среди
-
10. Приложение B: Вграждане на Git в приложения
- 10.1 Git от команден ред
- 10.2 Libgit2
- 10.3 JGit
- 10.4 go-git
- 10.5 Dulwich
-
A1. Приложение C: Git команди
- A1.1 Настройки и конфигурация
- A1.2 Издърпване и създаване на проекти
- A1.3 Snapshotting
- A1.4 Клонове и сливане
- A1.5 Споделяне и обновяване на проекти
- A1.6 Инспекция и сравнение
- A1.7 Дебъгване
- A1.8 Patching
- A1.9 Email команди
- A1.10 Външни системи
- A1.11 Административни команди
- A1.12 Plumbing команди
8.6 Git на ниско ниво - Транспортни протоколи
Транспортни протоколи
Git може да обменя данни между две хранилища по два основни начина: с “dumb” протокола и със “smart” протокола. Тук ще видим накратко как работят те.
Dumb протокол
Ако настройвате хранилище само за четене през HTTP, тогава dumb протоколът е вероятния избор.
Този вид протокол се нарича “dumb”, защото не изисква Git-specific код от страна на сървъра по време на транспортния процес. Процесът е серия от HTTP GET
заявки, при които клиентът може да предположи какво е разположението на Git хранилището в сървъра.
Забележка
|
Този вид протокол вече се използва сравнително рядко. При него е трудно да се гарантира защитата на данните, така че повечето Git хостове (cloud-based и on-premises) ще откажат използването му. Препоръчително е да се използва smart протокола, който ще разгледаме след малко. |
Нека преминем през http-fetch
процеса за библиотеката simplegit:
$ git clone http://server/simplegit-progit.git
Първото нещо, което прави командата, е да изтегли файла info/refs
.
Този файл се записва от командата update-server-info
, ето защо трябва да разрешите това като post-receive
hook за да може HTTP транспорта да работи коректно:
=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
Сега имате списък на отдалечените референции и SHA-1 стойности. След това поглеждаме към какво сочи референцията HEAD, така че да знаем какво да извлечем в работната директория, когато сме готови:
=> GET HEAD
ref: refs/heads/master
Това значи, че ще разпакетираме клона master
в края.
В този момент сме готови да пуснем процеса.
Понеже изходната точка е къмит обекта ca82a6
, който виждаме във файла info/refs
, започваме с неговото изтегляне:
=> GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
Получаваме обратно обект — този обект е в loose формат на сървъра и сме го изтеглили през статична HTTP GET заявка. Можете да използвате zlib за да го декомпресирате, да премахнете хедъра и да видите съдържанието на къмита:
$ 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
Следва да се изтеглят още два обекта — cfda3b
, което е дървото на съдържанието, към което сочи току що изтегления къмит, и родителския къмит 085bb3
:
=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
Това ни дава следващия къмит обект. Изтегляме tree обекта:
=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
Получаваме отговор 404 — изглежда, че tree обектът не е в loose формат на сървъра. За това може да има различни причини — обектът може да е в алтернативно хранилище или пък може да е в packfile в текущото. Git първо проверява за евентуални заместители:
=> GET objects/info/http-alternates
(empty file)
Ако получим списък от алтернативни URL-и, Git проверява за loose файлове и packfile файлове в тях — това е чудесен начин за проекти, които са forks на други да споделят обекти на диска.
Само че, в този случай нямаме такъв списък, така че обектът трябва да е пакетиран в packfile.
За да видим какви packfiles са налични на сървъра трябва да вземем файла objects/info/packs
, който съдържа списък с тях (генерира се също от update-server-info
):
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
Има само един packfile на сървъра, така че обектът очевидно е там, но все пак проверяваме и в индекса за да се уверим в това. Такава проверка е полезна и ако имате повече packfiles на сървъра за да намерите кой точно от тях съдържа търсения обект:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
Сега имаме packfile индекса и можем да видим дали обектът ни е посочен в него, индексът съдържа списък с SHA-1 чексумите и позициите (отместванията) на обектите от packfile файловете. Обектът ни е тук и следва да изтеглим целия packfile:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
Получихме tree обекта и можем да продължим стъпките през къмитите.
Те също са в packfile пакета, който току що изтеглихме, така че не се налага да правим повече заявки към сървъра.
Git извлича работно копие от клона master
, към който сочеше HEAD референцията изтеглена в началото.
Протоколът Smart
Протоколът dumb е прост, но не много ефективен и освен това не може да обслужва изпращането на данни от клиента към сървъра. Протоколът smart е много по-подходящ за трансфер на данни, но изисква процес в отдалечената страна на връзката, който да е запознат с Git — той трябва да може да прочита локалните данни, да определи какво има и от какво се нуждае клиента и да му генерира custom packfile като резултат. Съществуват две множества процеси за трансфер на данни: чифт за изпращане и още един чифт за приемане.
Изпращане на данни
За да изпраща данни към отдалечен процес, Git използва процесите send-pack
и receive-pack
.
Процесът send-pack
, очаквано, работи от страна на клиента и се свързва с receive-pack
процеса в другия край на връзката.
SSH
За пример, да кажем, че изпълнявате git push origin master
в проекта си и origin
е дефинирана като URL, за който се използва SSH протокола.
Git стартира send-pack
процеса, който инициира конекция към сървъра през SSH.
Този процес опитва да изпълни команда на сървъра през SSH повикване, което изглежда по подобен начин:
$ 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
незабавно отговаря с по един ред за всяка референция, която има — в този случай само клона master
и неговата SHA-1 чексума.
Първият ред също така подава списък с поддържаните от сървъра възможности (в случая report-status
, delete-refs
, и някои други, вкл. идентификация на клиента).
Данните се предават на части (chunks). Всяка част започва с 4-символна шестнайсетична стойност, която указва колко дълъг е остатъкът от нея (вкл. 4-те байта на стойността). Частите обикновено съдържат един ред данни следвани от символ за нов ред. Първата ви част започва с 00a5, което десетично е 165 и означава, че на този ред остават 165 байта. Следващият ред започва с 0000, което индикира, че сървърът няма какви други референции да показва.
След като вече знае статуса на сървъра, процесът send-pack
определя локалните къмити, които не присъстват на него.
За всяка референция, която това публикуване ще актуализира, процесът send-pack
подава на receive-pack
съответната информация.
Например, ако обновявате master
клона и добавяте клон experiment
, send-pack
отговорът може да изглежда така:
0076ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 \
refs/heads/master report-status
006c0000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d \
refs/heads/experiment
0000
Git изпраща по ред за всяка референция, която обновява съдържащ дължината на реда, старата SHA-1 стойност, новата SHA-1 стойност и името на съответната референция. Може да видите, че първият ред също така подава като информация поддържаните от клиента функционалности (report status). SHA-1 стойността състояща се само от нули означава, че там преди не е имало нищо — понеже добавяте липсващата до момента референция experiment. Ако изтривате референция, ще видите обратното, всички нули ще са в дясната SHA-1 чексума.
След това клиентът изпраща packfile на всички обекти, които сървърът няма. Последно, сървърът отговаря с индикация за успех или грешка:
000eunpack ok
HTTP(S)
Този процес в голямата си част е същия като HTTP, въпреки че handshaking механизмът е по-различен. Връзката се инициира с тази заявка:
=> 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
Това е краят на първата client-server обмяна.
Клиентът прави още една заявка, този път по метода POST
с данните, които send-pack
доставя.
=> POST http://server/simplegit-progit.git/git-receive-pack
Заявката POST
включва изхода от send-pack
и самия packfile.
Сървът след това отговаря с резултата за успех или отказ.
Имайте предвид, че HTTP протоколът може по-натам да постави тази информация в chunked трансферно кодиране.
Изтегляне на данни
Когато теглите данни, участват процесите fetch-pack
и upload-pack
.
Клиентът инициира fetch-pack
процес, който се свързва с upload-pack
такъв в другия край на връзката за да уговори какви данни ще се изтеглят.
SSH
Ако теглите през SSH, fetch-pack
изпълнява нещо подобно:
$ ssh -x git@server "git-upload-pack 'simplegit-progit.git'"
След като fetch-pack
се свърже, upload-pack
изпраща обратно нещо такова:
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
Това е много подобно на отговора, който връща receive-pack
, но функционалностите се различават.
Процесът връща в добавка къде сочи HEAD (symref=HEAD:refs/heads/master
), така че клиентът да знае какво да разпакетира, ако това е клониране.
На този етап процесът fetch-pack
проверява какви обекти има и отговаря с обектите, които му трябват изпращайки “want” и след това SHA-1 стойностите, които иска.
Той изпраща с “have” обектите, които има и техните SHA-1 стойности.
В края на този списък той подава “done” за да инструктира upload-pack
да започне изпращането на packfile пакета данни, който се търси:
003cwant ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009done
0000
HTTP(S)
Handshake механизмът за fetch операцията използва две HTTP заявки.
Първата е GET
към същия endpoint използван при dumb протокола:
=> 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
Това е много подобно на повикване към git-upload-pack
през SSH връзка, но втората размяна на данни се изпълнява като отделна заявка:
=> POST $GIT_URL/git-upload-pack HTTP/1.0
0032want 0a53e9ddeaddad63ad106860237bbf53411d11a7
0032have 441b40d833fdfa93eb2908e52742248faf0ee993
0000
Отново, това е същия формат като по-горе. Отговорът на тази заявка индикира успех или отказ и включва данните в packfile.
Обобщение
Тази секция съдържа много съкратен преглед на транспортните протоколи.
Протоколът включва и много други възможности като multi_ack
или side-band
поддръжка, но те са извън темата за книгата.
Опитахме да опишем основната идея в механизмите на комуникацията между клиент и сървър, ако се нуждаете от повече подробности, може да разгледате сорс кода на Git.