Когда и как Git использует дельты для хранения?
Чтение документации git. Одна из вещей, которые они часто подчеркивают, это то, что git хранит снимки, а не дельты. Поскольку я видел курс по Git, в котором говорится, что Git хранит различия между версиями файлов, я попробовал следующее: я инициализировал репозиторий git в пустой папке, создал файл lorem.txt
содержащий некоторый текст lorem ipsum установил файл и передал.
Затем с помощью find .git/objects -type f
В командной строке я перечислил, что git сохранил в папке объектов, и, как и ожидалось, нашел объект коммита, указывающий на объект дерева, указывающий на объект blob, содержащий текст lorem ispum, который я сохранил.
Затем я изменил текст lorem ipsum, добавив в него больше контента, поставил это изменение и подтвердил. Перечисляя файлы снова, я теперь мог видеть новый объект коммита, указывающий на новый три объекта и на новый объект BLOB. С помощью git cat-file -p 331cf0780688c73be429fa602f9dd99f18b36793
Я мог видеть содержимое нового блоба. Они были точно содержимым полного lorem.txt
файл, старое содержимое плюс изменения.
Это работает, как и ожидалось в документации: Git хранит снимки, а не дельты. Тем не менее, поиск в Интернете я нашел этот вопрос. На принятый ответ мы видим следующее:
Хотя это верно и важно на концептуальном уровне, это НЕ верно на уровне хранилища.
Git использует дельты для хранения.
Мало того, но он более эффективен, чем любая другая система. Поскольку он не хранит историю для каждого файла, когда он хочет выполнить дельта-сжатие, он берет каждый BLOB-объект, выбирает некоторые BLOB-объекты, которые могут быть похожими (используя эвристику, которая включает в себя самое близкое приближение предыдущей версии и некоторые другие), пытается генерировать дельты и выбирает самый маленький. Таким образом, он может (часто зависит от эвристики) использовать преимущества других похожих файлов или более старых версий, которые более похожи на предыдущие. Параметр "окно пакета" позволяет торговать производительность для качества дельта-сжатия. Значение по умолчанию (10) обычно дает приличные результаты, но когда пространство ограничено или для ускорения передачи по сети, git gc --aggressive использует значение 250, что делает его работу очень медленной, но обеспечивает дополнительное сжатие для данных истории.
Который говорит, что Git использует дельты для хранения. Как я понимаю из этого, Git не использует дельты все время, а только когда обнаруживает, что это необходимо. Это правда?
Я поместил много текста в файл, так что его размер составляет 2 МБ. Я думал, что при внесении небольшого изменения в большой текстовый файл Git будет автоматически использовать дельты, но, как я уже сказал, это не так.
Когда Git использует дельты и как это работает?
2 ответа
Git использует только дельты в "packfiles". Первоначально каждый объект git записывается в виде отдельного файла (как вы нашли). Позже, git может упаковать много объектов в один файл, называемый "файл упаковки". Затем файл пакета сжимается, что автоматически использует любые повторы между файлами в файле пакета (или повторения внутри файлов).
Эта упаковка выполняется git repack
, Вы можете увидеть это в действии, вызвав его вручную. Если вы бежите git repack -ad
в git-репо вы должны увидеть используемое дисковое пространство и количество файлов в .git/objects
падение, так как файлы объединяются в пакеты и сжимаются.
На практике вам обычно не нужно бежать git repack
, Git по умолчанию регулярно запускается git gc
который в свою очередь работает git repack
когда необходимо. Так что расслабься, у тебя твоя спина:-).
В превосходной "git book" также есть глава о пакетных файлах с дополнительными пояснениями: http://git-scm.com/book/en/v2/Git-Internals-Packfiles.
Git 2.18 (Q2 2018) документирует использование дельты в Documentation/technical/pack-format
См. Комм. 011b648(11 мая 2018 г.) Нгуен Тхай Нгук Дуй ( pclouds
)
(Объединено Юнио С Хамано - gitster
- в комитете b577198 от 23 мая 2018 года)
pack-format.txt: больше информации о формате файла пакета
Текущий документ упоминает
OBJ_*
константы без их фактических значений. Разработчик git знал бы, что они изcache.h
но это не очень дружелюбно для человека, который хочет прочитать этот файл для реализации парсера файла пакета.Точно так же разделенное представление вообще не задокументировано ("документ" - это в основном patch-delta.c). Перевести этот код C на английский с немного больше о том, что
ofs-delta
а такжеref-delta
имею в виду.
Итак, в документации сейчас говорится:
Типы объектов
Допустимые типы объектов:
OBJ_COMMIT
(1)OBJ_TREE
(2)OBJ_BLOB
(3)OBJ_TAG
(4)OBJ_OFS_DELTA
(6)OBJ_REF_DELTA
(7)Тип 5 зарезервирован для будущего расширения. Тип 0 недействителен.
Представительство
Концептуально существует только четыре типа объектов: коммит, дерево, тег и блоб.
Однако для экономии места объект может быть сохранен как "дельта" другого "базового" объекта.
Этим представлениям назначаются новые типы ss-delta и ref-delta, которые действительны только в файле пакета.И то и другое
ofs-delta
а такжеref-delta
сохранить "дельту", которая будет применена к другому объекту (называемому "базовым объектом") для реконструкции объекта.
Разница между ними в том,
- ref-delta напрямую кодирует 20-байтовое имя базового объекта.
- Если базовый объект находится в той же пачке, вместо этого ofs-delta кодирует смещение базового объекта в пачке.
Базовый объект также можно отделить, если он находится в той же упаковке.
Ref-delta также может относиться к объекту вне упаковки (то есть так называемой "тонкой упаковке"). Однако при хранении на диске пакет должен быть автономным, чтобы избежать циклической зависимости.Дельта-данные - это последовательность инструкций для восстановления объекта из базового объекта.
Если базовый объект разграничен, он должен быть сначала преобразован в каноническую форму. Каждая инструкция добавляет все больше и больше данных к целевому объекту, пока не будет завершена.
Пока есть две поддерживаемые инструкции:
- один для копирования байтового диапазона из исходного объекта и
- один для вставки новых данных, встроенных в саму инструкцию.
Каждая инструкция имеет переменную длину. Тип инструкции определяется седьмым битом первого октета. Следующие диаграммы соответствуют соглашению в RFC 1951 (формат сжатых данных Deflate).
С Git 2.20 (Q4 2018) искаженные или специально сформированные данные в packstream могут заставить наш код попытаться прочитать или записать после выделенного буфера и прервать вместо сообщения об ошибке, которая была исправлена.
t5303
: использоватьprintf
генерировать дельта-базыТочное количество байтов дельта-базового файла очень важно.
test-delta
помощник будет кормить егоpatch_delta()
, который будет barf, если он не соответствует размеру байта, указанному в дельте.
С помощью "echo
msgstr "может заканчиваться неожиданным окончанием строки на некоторых платформах (например,."\r\n
"вместо просто"\n
").Это на самом деле не приведет к сбою теста (так как мы уже ожидаем, что test-delta будет жаловаться на эти поддельные дельты), но это будет означать, что мы не выполняем код, которым себя считаем.
Давайте использовать
printf
вместо этого (которому мы уже доверяем, чтобы получить идеальный вывод, когда мы генерируем дельты).