Протоколы передачи данных
Git умеет передавать данные между репозиториями двумя способами: используя "глупый" и "умный" протоколы. В этой главе мы рассмотрим, как они работают.
Если вы разрешили доступ на чтение к вашему репозиторию через HTTP, то скорее всего будет использован "глупый" протокол. Протокол назвали глупым, потому что для его работы не требуется выполнение специфичных для Git операций на стороне сервера: весь процесс получения данных представляет собой серию HTTP GET запросов. При этом клиент ожидает обнаружить определённые файлы на сервере по заданным путям.
Глупый протокол довольно редко используется в наши дни. При использовании глупого протокола сложно обеспечить безопасность передачи и приватность данных, поэтому большинство Git серверов (как облачных, так и тех, что требуют установки) откажутся работать через него. Рекомендуется использовать умный протокол, который мы рассмотрим далее.
Давайте рассмотрим процесс получения данных из репозитория simplegit-progit:
$git clone http://server/simplegit-progit.git
Первым делом будет загружен файл info/refs. Данный файл записывается командой update-server-info, поэтому для корректной работы HTTP-транспорта необходимо выполнять её в post-receive триггере.
=> GET info/refs
ca82a6dff817ec66f44342007202690a93763949 refs/heads/master
Теперь у нас имеется список удалённых веток и их хеши. Далее, надо посмотреть, куда ссылается HEAD, чтобы знать на что переключиться после завершения работы команды.
=> GET HEAD
ref: refs/heads/master
Итак, нужно переключится на ветку master после окончания работы. На данном этапе можно начинать обход репозитория. Начальной точкой является коммит ca82a6, о чём мы узнали из файла info/refs, и мы начинаем с его загрузки:
=> GET objects/ca/82a6dff817ec66f44342007202690a93763949
(179 bytes of binary data)
Объект получен, он был в рыхлом формате на сервере, и мы получили его по HTTP, используя GET-запрос. Теперь можно его разархивировать, обрезать заголовок и посмотреть на содержимое:
$git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon 1205815931 -0700
committer Scott Chacon 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)
Если бы этот запрос вернул непустой список альтернатив, Git проверил бы указанные репозитории на наличие файла в "рыхлом" формате – довольно полезная фишка для проектов-форков, позволяющая устранить дублирование. Так как в данном случае альтернатив нет, объект, должно быть, упакован в pack-файле. Чтобы посмотреть доступные на сервере pack-файлы, нужно скачать файл objects/info/packs, содержащий их список. Этот файл тоже обновляется командой update-server-info:
=> GET objects/info/packs
P pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
На сервере имеется только один pack-файл, поэтому объект точно там, но необходимо проверить индексный файл, чтобы в этом убедиться. Если бы на сервере было несколько pack-файлов, загрузив сначала индексы, мы смогли бы определить, в каком именно pack-файле находится нужный нам объект:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.idx
(4k of binary data)
Теперь, когда мы получили индекс pack-файла, можно проверить, содержится ли в нём наш объект. Это возможно благодаря тому, что в индексе хранятся SHA-1 объектов, содержащихся внутри pack-файла, а также их смещения. Наш объект там присутствует, так что продолжим и скачаем весь pack-файл:
=> GET objects/pack/pack-816a9b2334da9953e530f27bcac22082a9f5b835.pack
(13k of binary data)
Итак, мы получили наше дерево, можно продолжить обход списка коммитов. Все они содержатся внутри свежескачанного pack-файла, так что снова обращаться к серверу не надо. Git извлекает рабочую копию ветки master, на которую ссылается HEAD (не забыли, для чего мы скачивали файл info/refs в самом начале?).
Глупый протокол прост, но неэффективен и не позволяет производить запись в удалённые репозитории. Гораздо чаще для обмена данными используют "умный" протокол. Но это требует запуска на сервере специального процесса, знающего о структуре Git репозитория, умеющего выяснять, какие данные необходимо отправить клиенту и генерирующего отдельный pack-файл с недостающими изменениями для него. Работу умного протокола обеспечивают несколько процессов: два для отправки данных на сервер и два для загрузки с него.
Читать дальше