Загрузка данных на сервер
Для загрузки данных на удалённый сервер используются процессы send-pack и receive-pack. Процесс send-pack запускается на клиенте и подключается к receive-pack на сервере.
Допустим, вы выполняете git push origin master и origin задан как URL, использующий протокол SSH. Git запускает процесс send-pack, который устанавливает соединение с сервером по протоколу SSH. Он пытается запустить команду на удалённом сервере через вызов ssh команды, который выглядит следующим образом:
$ssh -x git@server "git-receive-pack 'simplegit-progit.git'"
005bca82a6dff817ec66f4437202690a93763949 refs/heads/master report-status \
delete-refs side-band-64k quiet ofs-delta \
agent=git/2:2.1.1+github-607-gfba4028 delete-refs
003e085bb3bcb608e1e84b2432f8ecbe6306e7e7 refs/heads/topic
0000
Команда git-receive-pack тут же посылает в ответ по одной строке на каждую из имеющихся в наличии ссылок — в данном случае только ветку master и её SHA-1. Первая строка также содержит список возможностей сервера (здесь это report-status, delete-refs и парочка других, включая версию используемого процесса).
Каждая строка начинается с 4-байтового шестнадцатеричного значения, содержащего длину оставшейся части строки. Первая строка начинается с 005b, это 91 в десятичной системе счисления, значит в этой строке ещё 91 байт. Следующая строка начинается с 003e (62), то есть надо прочитать ещё 62 байта. Далее следует 0000, означающая конец списка ссылок.
Теперь, когда send-pack выяснил состояние сервера, он определяет коммиты, которые есть локально, но отсутствующие на сервере. Для каждой ссылки, которая будет обновлена командой push, процесс send-pack передаёт процессу receive-pack эти данные. Например, если мы обновляем ветку master, и добавляем ветку experiment, ответ send-pack будет выглядеть следующим образом:
0085ca82a6dff817ec66f44342007202690a93763949 15027957951b64cf874c3557a0f3547bd83b3ff6 \
refs/heads/master report-status
00670000000000000000000000000000000000000000 cdfdb42577e2506715f8cfeacdbabc092bf63e8d \
refs/heads/experiment
0000
Git посылает по строке, содержащей собственную длину, старый хэш, новый хэш и имя ссылки; для каждой обновляемой ссылки. В первой строке также посылаются возможности клиента. Хэш, состоящий из нулей, говорит о том, что раньше такой ссылки не было – вы ведь добавляете новую ветку experiment. При удалении ветки всё было бы наоборот: нули были бы справа.
Затем клиент посылает pack-файл c объектами, которых нет на сервере. В конце сервер передаёт статус операции – успех или ошибка:
000Aunpack ok
Этот процесс похож на HTTP, но установка соединения слегка отличается. Всё начинается с такого запроса:
=> GET http://server/simplegit-progit.git/info/refs?service=git-receive-pack
001f# service=git-receive-pack
000000ab6c5f0e45abd7832bf23074a333f739977c9e8188 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, передавая данные, полученные от команды git-upload-pack.
=> POST http://server/simplegit-progit.git/git-receive-pack
Этот запрос включает в себя результаты send-pack и собственно pack-файлы. Сервер, используя код состояния HTTP, возвращает результат операции.
Для получения данных из удалённых репозиториев используются процессы fetch-pack и upload-pack. Клиент запускает процесс fetch-pack, который подключается к процессу upload-pack на сервере для определения подлежащих передаче данных.
Если вы работаете через SSH, fetch-pack выполняет примерно такую команду:
$ssh -x git@server "git-upload-pack 'simplegit-progit.git'"
Как только fetch-pack подключается к upload-pack, тот отсылает обратно следующее:
00dfca82a6dff817ec66f44342007202690a93763949 HEADmulti_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
Это очень похоже на ответ receive-pack, но только возможности другие. Вдобавок upload-pack отсылает обратно ссылку HEAD, чтобы клиент понимал, на какую ветку переключиться, если выполняется клонирование.
На данном этапе процесс fetch-pack смотрит на объекты, имеющиеся в наличии, и для недостающих объектов отвечает словом "want" + соответствующий SHA-1. Для уже имеющихся объектов процесс отправляет их хеши со словом "have". В конце списка он пишет "done", и это даёт понять процессу upload-pack, что пора начинать отправлять упакованный pack-файл с необходимыми данными:
0054want ca82a6dff817ec66f44342007202690a93763949 ofs-delta
0032have 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
0000
0009done
"Рукопожатие" для процесса получения недостающих данных занимает два HTTP запроса. Первый — это GET запрос на тот же URL, что и в случае глупого протокола:
Читать дальше