-
1. Вступ
- 1.1 Про систему контролю версій
- 1.2 Коротка історія Git
- 1.3 Основи Git
- 1.4 Git, зазвичай, тільки додає дані
- 1.5 Три стани
- 1.6 Командний рядок
- 1.7 Інсталяція Git
- 1.8 Початкове налаштування Git
- 1.9 Отримання допомоги
- 1.10 Підсумок
-
2. Основи Git
- 2.1 Створення Git-репозиторія
- 2.2 Запис змін до репозиторія
- 2.3 Перегляд історії комітів
- 2.4 Скасування речей
- 2.5 Взаємодія з віддаленими сховищами
- 2.6 Теґування
- 2.7 Псевдоніми Git
- 2.8 Підсумок
-
3. Галуження в git
- 3.1 Гілки у кількох словах
- 3.2 Основи галуження та зливання
- 3.3 Управління гілками
- 3.4 Процеси роботи з гілками
- 3.5 Віддалені гілки
- 3.6 Перебазовування
- 3.7 Підсумок
-
4. Git на сервері
- 4.1 Протоколи
- 4.2 Отримання Git на сервері
- 4.3 Генерація вашого публічного ключа SSH
- 4.4 Налаштування Серверу
- 4.5 Демон Git
- 4.6 Розумний HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Варіанти стороннього хостингу
- 4.10 Підсумок
-
5. Розподілений Git
-
6. GitHub
-
7. Інструменти Git
- 7.1 Вибір ревізій
- 7.2 Інтерактивне індексування
- 7.3 Ховання та чищення
- 7.4 Підписання праці
- 7.5 Пошук
- 7.6 Переписування історії
- 7.7 Усвідомлення скидання (reset)
- 7.8 Складне злиття
- 7.9 Rerere
- 7.10 Зневадження з Git
- 7.11 Підмодулі
- 7.12 Пакування
- 7.13 Заміна
- 7.14 Збереження посвідчення (credential)
- 7.15 Підсумок
-
8. Налаштування Git
-
9. Git and Other Systems
- 9.1 Git як клієнт
- 9.2 Міграція на Git
- 9.3 Підсумок
-
10. Git зсередини
- 10.1 Кухонні та парадні команди
- 10.2 Об’єкти Git
- 10.3 Посилання Git
- 10.4 Файли пакунки
- 10.5 Специфікація посилань (refspec)
- 10.6 Протоколи передачі
- 10.7 Супроводження та відновлення даних
- 10.8 Змінні середовища
- 10.9 Підсумок
-
A1. Додаток A: Git в інших середовищах
- A1.1 Графічні інтерфейси
- A1.2 Git у Visual Studio
- A1.3 Git в Eclipse
- A1.4 Git у Bash
- A1.5 Git у Zsh
- A1.6 Git у Powershell
- A1.7 Підсумок
-
A2. Додаток B: Вбудовування Git у ваші застосунки
- A2.1 Git з командного рядка
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
-
A3. Додаток C: Команди Git
- A3.1 Налаштування та конфігурація
- A3.2 Отримання та створення проектів
- A3.3 Базове збереження відбитків
- A3.4 Галуження та зливання
- A3.5 Поширення й оновлення проектів
- A3.6 Огляд та порівняння
- A3.7 Зневаджування
- A3.8 Латання (patching)
- A3.9 Електронна пошта
- A3.10 Зовнішні системи
- A3.11 Адміністрування
- A3.12 Кухонні команди
10.6 Git зсередини - Протоколи передачі
Протоколи передачі
Git може передавати дані між двома репозиторіями двома головними способами: тупим'' протоколом та
розумним''.
У цій секції швидко розглянемо, як ці два головні протоколи працюють.
Тупий протокол
Якщо ви налаштовуєте репозиторій, щоб він був доступним лише для читання через HTTP, то, напевно, ви використаєте тупий протокол.
Цей протокол називається `тупим'', оскільки він не вимагає жодного специфічного для Git коду з боку сервера впродовж процесу передачі; процес отримання даних — це просто низка HTTP запитів `GET
, в яких клієнти можуть припустити, як розташовано репозиторій Git на сервері.
Зауваження
|
Нині тупий протокол використовується доволі зрідка. Його важко зробити безпечним чи приватним, отже більшість серверів розгортання Git (як хмарних, як і особистих) відмовляються ним користуватись. Зазвичай варто використовувати розумний протокол, який буде описано трохи далі. |
Прослідкуймо за процесом http-fetch
для бібліотеки simplegit:
$ git clone http://server/simplegit-progit.git
Спершу ця команда отримає файл info/refs
.
Цей файл записується командою update-server-info
, тому вам треба ввімкнути її в гаку post-receive
, щоб 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)
Ви отримуєте об’єкт – цей об’єкт знаходиться у вільному форматі на сервері, і ви отримали його статичним запитом 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
changed the version number
Далі, вам треба отримати ще два об’єкти – cfda3b
, який є деревом вмісту, на який вказує щойно отриманий коміт; а також 085bb3
, який є батьківським комітом:
=> GET objects/08/5bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
(179 bytes of data)
Це надає нам об’єкт наступного коміту. Хапайте об’єкт дерева:
=> GET objects/cf/da3bf379e4f8dba8717dee55aab78aef7f4daf
(404 - Not Found)
Овва – здається, цього об’єкта дерева немає у вільному форматі на сервері, тому ви отримали відповідь 404. Є декілька причин для цього – об’єкт може бути в альтернативному сховищі, або він може бути у файлі пакунку цього репозиторія. Git перевіряє спочатку задані альтернативи:
=> GET objects/info/http-alternates
(empty file)
Якщо це поверне список альтернативних URL, Git перевірить вільні файли та пакунки там – це зручний засіб для проектів, які є форками інших, щоб спільно користуватись об’єктами на диску.
Втім, оскільки в даному випадку жодної альтернативи не зазначено, ваш об’єкт має бути у файлі пакунків.
Щоб побачити, чи існує файл пакунків на цьому сервері, вам треба отримати файл objects/info/packs
, який містить їх список (також згенерований командою update-server-info
):
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
Існує лише один файл пакунок на сервері, отже, очевидно, ваш об’єкт знаходиться там, проте ви перевірите файл індексу, щоб переконатись. Це також корисно, якщо у вас декілька файлів пакунків на сервері, щоб ви могли побачити, який з них містить потрібний об’єкт:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
Тепер, коли у вас є індекс пакунку, ви можете перевірити, чи є там ваш об’єкт – адже індекс надає список SHA-1 сум об’єктів, які містяться в пакунку, та зсуви до них. Ваш об’єкт там, отже уперед отримувати весь пакунок:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
У вас є об’єкт дерева, отже ви продовжуєте обходити ваші коміти.
Вони всі також у щойно завантаженому пакунку, отже вам не доводиться більше робити запитів до сервера.
Git створює робочу копію гілки master
, на яку вказувало посилання HEAD, яке ви завантажили спочатку.
Розумний протокол
Тупий протокол простий, проте нефективний, та не може писати дані клієнта до сервера. Розумний протокол є більш поширеним методом передачі даних, проте вимагає, щоб на віддаленому сервері був процес, який знає про Git – він може читати локальні дані, зрозуміти, що потрібно клієнту, та згенерувати окремий пакунок для нього. Є два набори процесів для передачі даних: пара для відвантаження даних та пара для завантаження даних.
Відвантаження даних
Щоб відвантажити дані до віддаленого процесу, 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
, а також деякі інші, включно з ідентифікатором клієнта).
Кожен рядок починається з чотирисимвольного шістнадцяткового значення, яке задає, наскільки довгою є решта рядка. Ваш перший рядок починається з 00a5, що шістнадцятковою означає 165, тобто в цьому рядку ще 165 байтів. Наступний рядок 0000, що означає, що сервер закінчив перелічування посилань.
Тепер, коли send-pack
знає стан сервера, він може визначити, які коміти в нього є, а у сервера немає.
Для кожного посилання, яке цей push буде оновлювати, процес 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, та посиланням, яке оновлюється. Перший рядок також містить можливості клієнта. Значення SHA-1 зі всіма нулями означає, що раніше нічого не було – оскільки ви додаєте посилання experiment. Якщо ви вилучаєте посилання, то буде навпаки: всі нулі з правого боку.
Далі, клієнт надсилає пакунок, що містить усі об’єкти, яких ще немає на сервері. Нарешті, сервер відповідає успіхом чи невдачею.
000eunpack ok
HTTP(S)
Цей процес майже такий самий через HTTP, хоча квитування трохи інше. Зв’язок починається з такого запиту:
=> 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
Це кінець першого обміну клієнта сервера.
Клієнт потім робить ще один запит, цього разу POST
, з даними, які надає send-pack
.
=> POST http://server/simplegit-progit.git/git-receive-pack
Запит POST
включає вивід send-pack
, а також пакунок, як тіло запиту.
Сервер потім зазначає успіх чи провал за допомогою відповіді HTTP.
Завантаження даних
Коли ви завантажуєте дані, задіяно процеси 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
почав надсилати пакунок з необхідними даними:
003cwant ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0009done
0000
HTTP(S)
Квитування для операції отримання потребує двох HTTP запитів.
Перший — це GET
до тієї ж кінцевої точки, яку використовував тупий протокол:
=> 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
Знову, це той самий формат, що й вище. Відповідь на цей запит містить успіх або провал, та включає пакунок.
Підсумок щодо протоколів
Друга секція містить дуже базовий огляд протоколів передачі.
Протокол включає багато іншого функціоналу, такого як можливості multi_ack
чи sde-band
, проте їх розгляд виходить за межі цієї книги.
Ми намагались дати вам розуміння загальної взаємодії між клієнтом та сервером; якщо вам потрібно більше інформації, то ви напевно забажаєте подивитись у вихідний код Git.