частичное клонирование
Функция «Частичное клонирование» — это оптимизация производительности для Git, которая позволяет Git работать без полной копии репозитория. Цель этой работы — позволить Git лучше обрабатывать чрезвычайно большие репозитории.
Во время операций клонирования и получения Git загружает полное содержимое и историю репозитория. Это включает все коммиты, деревья и объекты для всей жизни репозитория. Для чрезвычайно больших репозиториев клонирование может занимать часы (или дни) и потреблять более 100 ГБ дискового пространства.
Часто в таких репозиториях много объектов (blobs и деревьев), которые пользователю не нужны, например:
-
файлы за пределами рабочей области пользователя в дереве. Например, в репозитории с 500 000 директорий и 3,5 млн файлов в каждом коммите мы можем избежать загрузки многих объектов, если пользователю нужен только узкий «конус» исходного дерева.
-
большие бинарные ресурсы. Например, в репозитории, где большие артефакты сборки заносятся в дерево, мы можем избежать загрузки всех предыдущих версий этих несливающихся бинарных ресурсов и загрузить только те версии, которые фактически ссылаются.
Частичное клонирование позволяет избежать загрузки таких ненужных объектов заранее во время операций клонирования и получения, тем самым сокращая время загрузки и использование дискового пространства. Отсутствующие объекты могут быть позже «по запросу» загружены, если/когда это потребуется.
Удаленный репозиторий, который позже может предоставить отсутствующие объекты, называется удаленным репозиторием-обеспечителем (promisor remote), так как он обещает отправить объекты по запросу. Изначально Git поддерживал только один удаленный репозиторий-обеспечителя, удаленный репозиторий origin, из которого пользователь клонировал и который был настроен в параметре конфигурации «extensions.partialClone». Позже была реализована поддержка более одного удаленного репозитория-обеспечителя.
Использование частичного клонирования требует, чтобы пользователь был онлайн, и удаленный репозиторий origin или другие удаленные репозитории-обеспечители были доступны для получения недостающих объектов по запросу. Это может или не может быть проблематично для пользователя. Например, если пользователь может оставаться в предварительно выбранном подмножестве исходного дерева, он может не столкнуться с отсутствующими объектами. В качестве альтернативы пользователь может попробовать предварительно получить различные объекты, если знает, что он будет офлайн.
Нецелевые задачи
Частичное клонирование — это механизм для ограничения числа загружаемых blobs и деревьев в заданном диапазоне коммитов — и поэтому независимо от и не предназначено для конфликтов с существующими механизмами ограничения набора запрашиваемых коммитов (т.е. поверхностное клонирование, отдельный ветвь или загрузка <refspec>).
Обзор дизайна
Частичное клонирование логически состоит из следующих частей:
-
Механизм для клиента для описания ненужных или нежелательных объектов серверу.
-
Механизм для сервера для исключения таких нежелательных объектов из пакетов, отправляемых клиенту.
-
Механизм для клиента для корректной обработки отсутствующих объектов (которые были предварительно исключены сервером).
-
Механизм для клиента для заполнения отсутствующих объектов по мере необходимости.
Подробности дизайна
-
Новая возможность протокола пакетов «фильтр» добавляется к переговорам fetch-pack и upload-pack.
Это использует существующий механизм обнаружения возможностей. См. «фильтр» в gitprotocol-pack[5].
-
Клиенты передают «спецификацию фильтра» для клонирования и получения, которая передается серверу для запроса фильтрации во время построения пакетов.
Доступны различные фильтры, чтобы удовлетворить различные ситуации. См. «--filter=<filter-spec>» в Documentation/rev-list-options.txt.
-
На сервере pack-objects применяет запрошенную спецификацию фильтра, создавая «отфильтрованные» пакеты для клиента.
Эти отфильтрованные пакеты являются неполными в традиционном смысле, потому что они могут содержать объекты, которые ссылаются на объекты, не содержащиеся в пакете и которые у клиента еще нет. Например, отфильтрованный пакет может содержать деревья или теги, которые ссылаются на отсутствующие blobs или коммиты, которые ссылаются на отсутствующие деревья.
-
На клиенте эти неполные пакеты помечаются как «пакеты-обеспечители» и обрабатываются различными командами по-разному.
-
Для локального репозитория добавляется расширение конфигурации, чтобы предотвратить сбой старых версий git во время операции из-за отсутствующих объектов, с которыми они не могут справиться. См.
extensions.partialCloneв git-config[1].
Обработка отсутствующих объектов
-
Объект может отсутствовать из-за частичного клонирования или получения, или из-за повреждения репозитория. Чтобы отличить эти случаи, локальный репозиторий специально указывает такие отфильтрованные пакеты, полученные от удаленных репозиториев-обеспечителей, как «пакеты-обеспечители».
Эти пакеты-обеспечители состоят из файла «<name>.promisor» с произвольным содержимым (например, файлы «<name>.keep»), помимо файлов «<name>.pack» и «<name>.idx».
-
Локальный репозиторий рассматривает «объект-обеспечителя» как объект, о котором он знает (насколько ему известно), что удаленные репозитории-обеспечители пообещали, что у них есть, либо потому, что локальный репозиторий имеет этот объект в одном из своих пакетов-обеспечителей, либо потому, что другой объект-обеспечителя на него ссылается.
Когда Git сталкивается с отсутствующим объектом, Git может проверить, является ли он объектом-обеспечителем, и обработать его соответствующим образом. В противном случае Git может сообщить о повреждении.
Это означает, что нет необходимости для клиента явным образом поддерживать дорогостоящий список отсутствующих объектов.[a]
-
Поскольку почти весь код Git в настоящее время ожидает, что любой ссылающийся объект будет присутствовать локально, и мы не хотим принуждать каждую команду к выполнению предварительного прогона, добавляется механизм обратной совместимости, чтобы разрешить Git попытаться динамически загрузить отсутствующие объекты из удаленных репозиториев-обеспечителей.
Когда обычный поиск объекта не находит объект, Git вызывает promisor_remote_get_direct(), чтобы попытаться получить объект из удаленного репозитория-обеспечителя, а затем повторить поиск объекта. Это позволяет объектам «загружаться по запросу» без сложных алгоритмов предсказания.
Из соображений эффективности не выполняется проверка, является ли отсутствующий объект на самом деле объектом-обеспечителем.
Динамическая загрузка объектов, как правило, медленная, так как объекты загружаются по одному.
-
checkout(и любая другая команда, использующаяunpack-trees) научилась предварительно загружать все необходимые отсутствующие blobs в одной партии. -
rev-listнаучилась печатать отсутствующие объекты.Это можно использовать другим командам для предварительной загрузки объектов в пакет. Например, «git log -p A..B» может внутри сначала выполнить что-то вроде «git rev-list --objects --quiet --missing=print A..B» и предварительно загрузить эти объекты в пакет.
-
fsckбыла обновлена, чтобы полностью учитывать объекты-обеспечителя. -
repackв GC была обновлена, чтобы вообще не касаться пакетов-обеспечителей и переупаковывать только другие объекты. -
Глобальная переменная «fetch_if_missing» используется для управления тем, будет ли поиск объекта пытаться динамически загрузить отсутствующий объект или сообщить об ошибке.
Мы не довольны этой глобальной переменной и хотели бы ее удалить, но для этого требуется значительная рефакторинг кода объекта для передачи дополнительного флага.
Загрузка отсутствующих объектов
-
Загрузка объектов выполняется путем вызова подпроцесса «git fetch».
-
Локальный репозиторий отправляет запрос с хэшами всех запрашиваемых объектов и не выполняет никаких переговоров о пакете. Затем он получает пакет.
-
Поскольку мы повторно используем существующий механизм загрузки, загрузка в настоящее время загружает все объекты, на которые ссылаются запрошенные объекты, даже если они не нужны.
-
Загрузка с
--refetchзапросит совершенно новый отфильтрованный пакет от удаленного репозитория, который можно использовать для изменения фильтра, не прибегая к динамической загрузке отсутствующих объектов.
Использование многих удаленных репозиториев-обеспечителей
Можно настроить и использовать несколько удаленных репозиториев-обеспечителей.
Это позволяет, например, пользователю иметь несколько географически близких кэшированных серверов для загрузки отсутствующих blobs, продолжая выполнять отфильтрованные git-fetch команды с центрального сервера.
При загрузке объектов удаленные репозитории-обеспечители пытаются один за другим, пока все объекты не будут загружены.
Удаленные репозитории, которые считаются «удаленными репозиториями-обеспечителями», указаны следующими переменными конфигурации:
-
extensions.partialClone = <name> -
remote.<name>.promisor = true -
remote.<name>.partialCloneFilter = ...
Только один удаленный репозиторий-обеспечителя можно настроить с помощью переменной конфигурации extensions.partialClone. Этот удаленный репозиторий-обеспечителя будет последним, который будет проверен при загрузке объектов.
Мы решили сделать его последним, потому что, скорее всего, тот, кто использует множество удаленных репозиториев-обеспечителей, делает это потому, что другие удаленные репозитории-обеспечители лучше по каким-то причинам (возможно, они ближе или быстрее для некоторых объектов), чем origin, и origin, вероятно, является удаленным репозиторием, указанным в extensions.partialClone.
Это обоснование не очень сильное, но нужно было сделать выбор, и в любом случае, долгосрочный план должен заключаться в том, чтобы сделать порядок каким-то образом полностью настраиваемым.
Однако пока другие удаленные репозитории-обеспечители будут проверяться в том порядке, в котором они появляются в файле конфигурации.
Текущие ограничения
-
Невозможно указать порядок проверки удаленных репозиториев-обеспечителей иным способом, кроме как в том порядке, в котором они появляются в файле конфигурации.
Также невозможно указать порядок, который используется при получении из одного репозитория, и другой порядок при получении из другого репозитория.
-
Невозможно отправить только определенные объекты в удаленный репозиторий-обеспечителя.
Невозможно одновременно отправлять в несколько удаленных репозиториев-обеспечителей в определенном порядке.
-
Динамическая загрузка объектов будет запрашивать у удаленных репозиториев-обеспечителей только отсутствующие объекты. Мы предполагаем, что удаленные репозитории-обеспечители имеют полное представление о репозитории и могут удовлетворить все такие запросы.
-
Переупаковка в основном обрабатывает пакеты-обеспечители и пакеты, не являющиеся пакетами-обеспечителями, как 2 отдельных раздела и не смешивает их.
-
Динамическая загрузка объектов вызывает fetch-pack один раз для каждого элемента, потому что большинство алгоритмов натыкаются на отсутствующий объект и нуждаются в его решении, прежде чем продолжить свою работу. Это может привести к значительной нагрузке — и нескольким запросам аутентификации — если требуется много объектов.
-
Динамическая загрузка объектов в настоящее время использует существующий протокол пакетов V0, что означает, что каждый объект запрашивается через fetch-pack. Сервер отправит полный набор info/refs при установлении подключения. Если есть большое количество refs, это может привести к значительной нагрузке.
Планы на будущее
-
Улучшить способ указания порядка, в котором будут пробоваться удаленные серверы promisor.
Например, это позволит явно указать: «При получении данных с этого удаленного сервера я хочу использовать эти удалённые серверы promisor в этом порядке, однако при отправке или получении данных на тот удаленный сервер я хочу использовать эти удаленные серверы promisor в другом порядке».
-
Разрешить отправку данных на удаленные серверы promisor.
Пользователь может захотеть работать в треугольной рабочей схеме с несколькими удаленными серверами promisor, каждый из которых имеет неполное представление о репозитории.
-
Разрешить фильтрацию, не основанную на пути, для использования битовых карт packfile (если они доступны). Это была просто ошибка при первоначальной реализации.
-
Исследовать использование долговременного процесса для динамического получения ряда объектов, как предложено в [5,6], чтобы уменьшить затраты на запуск процесса и накладные расходы.
Было бы неплохо, если бы протокол pack версии 2 позволил этому долговременному процессу выполнять серию запросов через одно долговременное подключение.
-
Исследовать протокол pack версии 2, чтобы избежать трансляции информации/ссылок при каждом подключении к серверу для динамического получения отсутствующих объектов.
-
Исследовать необходимость обработки разрозненных объектов promisor.
Объекты в файлах promisor packfile могут ссылаться на отсутствующие объекты, которые можно динамически получить с сервера. Было предположено, что разрозненные объекты создаются только локально и, следовательно, не должны ссылаться на отсутствующий объект. Возможно, нам придётся пересмотреть это предположение, если, например, мы динамически получим отсутствующее дерево и сохраним его как разрозненный объект, а не в отдельном файле packfile.
Это не обязательно означает, что нам нужно помечать разрозненные объекты как promisor; может быть достаточно ослабить функции поиска объектов или функции «is-promisor».
Незадачи
-
Всякий раз, когда поднимается вопрос о «загрузке блоков по требованию», кажется, что кто-то предлагает серверу «угадать» и отправить дополнительные объекты, которые могут быть связаны с запрошенными объектами.
Никакой работы по этому вопросу не проводилось; мы просто документируем, что это распространённое предложение. Мы не уверены, как это будет работать, и у нас нет планов работать над этим.
Сервер может отправлять больше объектов, чем запрошено (даже для динамического получения объектов), но мы не разрабатываем это.
Примечания
[a] Список отсутствующих объектов, требующих дорогостоящей модификации: Ранее при разработке частичного клонирования мы обсуждали необходимость единого списка отсутствующих объектов. Это по существу будет отсортированный линейный список OID, которые были пропущены сервером во время клонирования или последующих извлечений.
Этот файл необходимо загрузить в память при каждом поиске объекта. Его необходимо читать, обновлять и перезаписывать (как .git/index) при каждом явном «git fetch» и при любом динамическом извлечении объекта.
Стоимость чтения, обновления и записи этого файла может добавить существенные накладные расходы к каждой команде, если отсутствует много объектов. Например, если отсутствует 100 млн блоков, этот файл будет занимать не менее 2 ГБ на диске.
С концепцией «promisor» мы предполагаем отсутствие объекта на основе типа файла packfile, который на него ссылается.
Связанные ссылки
[0] https://crbug.com/git/2 Баг#2: Частичное клонирование
[1] https://lore.kernel.org/git/20170113155253.1644-1-benpeart@microsoft.com/
Тема: [RFC] Добавление поддержки загрузки блоков по требованию
Дата: Пт, 13 янв 2017 10:52:53 -0500
[2] https://lore.kernel.org/git/cover.1506714999.git.jonathantanmy@google.com/
Тема: [PATCH 00/18] Частичное клонирование (от клонирования к ленивой загрузке за 18 патчей)
Дата: Пт, 29 сен 2017 13:11:36 -0700
[3] https://lore.kernel.org/git/20170426221346.25337-1-jonathantanmy@google.com/
Тема: Предложение по поддержке отсутствующих блоков в репозиториях Git
Дата: Ср, 26 апр 2017 15:13:46 -0700
[4] https://lore.kernel.org/git/1488999039-37631-1-git-send-email-git@jeffhostetler.com/
Тема: [PATCH 00/10] RFC Частичное клонирование и извлечение
Дата: Ср, 8 мар 2017 18:50:29 +0000
[5] https://lore.kernel.org/git/20170505152802.6724-1-benpeart@microsoft.com/
Тема: [PATCH v7 00/10] переработка кода процесса фильтрации в модуль повторного использования
Дата: Пт, 5 мая 2017 11:27:52 -0400
[6] https://lore.kernel.org/git/20170714132651.170708-1-benpeart@microsoft.com/
Тема: [RFC/PATCH v2 0/1] Добавление поддержки загрузки блоков по требованию
Дата: Пт, 14 июл 2017 09:26:50 -0400
© 2005–2026 Linus Torvalds and others
Licensed under the GNU General Public License version 2.
https://git-scm.com/docs/partial-clone