Стандартизирован ли алгоритм git для двоичных различий (дельта-хранилище)? - PullRequest
50 голосов
/ 28 февраля 2012

Git использует дельта-сжатие для хранения объектов, похожих друг на друга.

Этот алгоритм стандартизирован и используется также в других инструментах? Есть ли документация, описывающая формат? Совместимо ли это с xdelta / VCDIFF / RFC 3284?

Ответы [ 3 ]

64 голосов
/ 28 февраля 2012

Я думаю, что дифференциальный алгоритм, используемый для файлов пакета , был связан с одной из дельта-кодировкой там: первоначально (2005) xdelta , а затем libXDiff .
Но затем, как подробно описано ниже, он перешел к пользовательской реализации.

В любом случае, как указано здесь :

Git выполняет делитирование * только 1017 * в пакетных файлах.
Но когда вы нажимаете через SSH, git генерирует файл с коммитами, а другая сторона не есть, и эти пакеты являются тонкими пакетами, поэтому они также имеют дельты ... но удаленная сторона затем добавляет базы к этим тонким пакетам, делая их автономными.

(примечание: создание большого количества файлов пакета или извлечение информации из огромного файла пакета дорого и объясняет, почему git плохо обрабатывает огромные файлы или огромное хранилище.
См. Больше в " git с большими файлами ")

Эта тема также напоминает нам:

На самом деле, как я помню и понимаю, на самом деле упаковочные файлы и дельтификации ( LibXDiff, а не xdelta ) изначально были из-за пропускной способности сети (что намного дороже, чем дисковое пространство), и производительность ввода-вывода при использовании одного файла mmapped вместо очень большого количества незакрепленных объектов.

LibXDiff упоминается в этой ветке 2008 .

Однако с тех пор алгоритм эволюционировал, возможно, в пользовательском, как этот поток 2011 иллюстрирует , и как указывает заголовок diff-delta.c:

Итак, строго говоря, текущий код в Git не имеет никакого сходства с кодом libxdiff вообще.
Однако основной алгоритм обеих реализаций одинаков
.
Изучить версию libxdiff, вероятно, проще, чтобы понять, как это работает.

/*
 * diff-delta.c: generate a delta between two buffers
 *
 * This code was greatly inspired by parts of LibXDiff from Davide Libenzi
 * http://www.xmailserver.org/xdiff-lib.html
 *
 * Rewritten for GIT by Nicolas Pitre <nico@fluxnic.net>, (C) 2005-2007
 */

Подробнее о упаковочных файлах Git Book :

packfile format


Git 2.18 добавляет к дельта-описанию в этом новом разделе документации , который сейчас (Q2 2018) гласит:

Типы объектов

Допустимые типы объектов:

  • 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).

22 голосов
/ 13 января 2013

Дельта-кодирование Git основано на копировании / вставке.

Это означает, что производный файл закодирован как последовательность кодов операций, которые могут представлять инструкции копирования (например: копирование из базового файла y байтов, начиная со смещения xв целевой буфер) или вставьте инструкции (например: вставьте следующие x байтов в целевой буфер).

В качестве очень простого примера (взятого из статьи «Поддержка файловой системы для дельта-сжатия»), учтите, чтомы хотим создать дельта-буфер для преобразования текста «кэш-прокси» в «кэш-прокси».Результирующие инструкции должны быть:

  1. Копировать 5 байтов из смещения 7 (скопировать 'кеш' из базового буфера)
  2. Вставить два пробела
  3. Копировать 5 байтов из смещения0 (скопировать 'прокси' из базового буфера)

Что переводится в кодировку git:

(байты 1-3 представляют первую инструкцию)

  • 0x91 (10010001), который разбивается на
    • 0x80 (10000000) (набор старших значащих битов делает это «копированием из базы на вывод»)
    • 0x01 (00000001) (означает «передвиньте один байт и используйте его в качестве базового смещения)
    • 0x10 (00010000) (продвиньте один байт и используйте его в качестве длины)
  • 0x07 (смещение)
  • 0x05 (длина)

(байты 4-6 представляют вторую инструкцию)

  • 0x02 (поскольку MSB не установлен, это означает «вставить следующуюдва байта в выходной файл ')
  • 0x20 (пробел)
  • 0x20 (пробел)

(байты 7-8 представляют последнюю инструкцию)

  • 0x90 (10010000), который разбивается на
    • 0x80 (10000000) (означает «копировать»)
    • 0x10 (00010000) (передвиньте один байт и используйте егокак длина)
  • 0x05 (длина)

Обратите внимание, что в последней инструкции копирования не указано смещение, что означает смещение 0. Другие биты в копииКод операции также может быть установлен, когда требуются большие смещения / длины.

В этом примере результирующий дельта-буфер имеет 8 байтов, что не слишком много для сжатия, поскольку целевой буфер имеет 12 байтов, но когда это кодированиеПрименительно к большим текстовым файлам это может иметь огромное значение.

Я недавно отправил библиотеку node.js в github, которая реализует обе функции diff / patch с использованием дельта-кодирования git.Код должен быть более читабельным и комментируемым, чем код в git source, который сильно оптимизирован.

Я также написал несколько тестов , которые объясняют выходные коды операцийиспользуется в каждом примере в формате, аналогичном приведенному выше.

4 голосов

Этот алгоритм стандартизирован и используется также в других инструментах?

Формат пакета является частью общедоступного API: протоколы передачи, используемые для операций push и fetch, используют его для отправки меньшего количества данных по сети.

Они реализованы как минимум в двух других основных реализациях Git, кроме ссылки: JGit и libgit2 .

Следовательно, очень маловероятно, что в формат будут внесены обратно несовместимые изменения, и его можно считать «стандартизированным» в этом смысле.

Этот удивительный файл из документации описывает эвристику, использованную в алгоритме пакета, как забавный комментарий Линуса: https://github.com/git/git/blob/v2.9.1/Documentation/technical/pack-heuristics.txt

...