Git Делить файлы файлов, а не снимки?

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

В отличие от этого, Git хранит полный снимок всего проекта в каждой ревизии. Причина этого не в том, что размер репо резко возрастает с каждым фиксацией. Каждый файл в проекте хранится в виде файла в подкаталоге Git, названном для хеша его содержимого. Поэтому, если содержимое не изменилось, хэш не изменился, и фиксация просто указывает на тот же файл. И есть и другие оптимизации.

Все это имело смысл для меня, пока я не наткнулся на эту информацию о файлах пакетов, в которые Git периодически помещает данные для экономии места:

Чтобы сохранить это пространство, Gitиспользует пакетный файл. Это формате, где Git сохранит только часть, которая изменилась во второй файла с указателем на файл, который аналогично.

Разве это в основном не возвращается к хранению дельт? Если нет, то как оно отличается? Как это избежать, подвергая Git тем же проблемам, что и другие системы управления версиями?

Например, Subversion использует дельта, а откат 50 версий означает отмену 50 разностей, тогда как Git вы можете просто захватить соответствующий снимок. Если Git также не хранит 50 diff в файлах packfiles... есть ли какой-то механизм, который говорит "после небольшого количества дельт, мы будем хранить весь новый снимок", чтобы мы не накапливали слишком большой набор изменений? Как еще Git избежать недостатков дельт?

Ответ 1

Резюме:
Файлы Git s pack тщательно сконструированы для эффективного использования дисковых кэшей и обеспечить "хорошие" шаблоны доступа для общих команд и для чтения недавно упомянутых объекты.


Git s пакетный файл формат довольно гибкий (см. Documentation/technical/pack-format.txt, или Packfile в Git Community Book). Файлы пакетов хранят объекты в двух основных Способы: "неинтегрированные" (берут данные исходного объекта и сбрасывают-сжимают это) или "deltified" (сформировать дельта против какого-либо другого объекта, тогда deflate-compress результирующие данные дельта). Объекты, хранящиеся в пакет может быть в любом порядке (они не обязательно (обязательно) должны быть сортировка по типу объекта, имени объекта или любому другому атрибуту) и деликатные объекты могут быть сделаны против любого другого подходящего объекта того же типа.

Команда

Git s pack-objects использует несколько heuristics для обеспечить отличную местность ссылок для общих команды. Эти эвристики управляют как выбором базы объекты для дефинированных объектов и порядок объектов. каждый механизм в основном независим, но они разделяют некоторые цели.

Git образует длинные цепочки дельта-сжатых объектов, но эвристика пытается убедиться, что только "старые" объекты находятся на концах длинные цепочки. Кэш дельта-базы (размер которого контролируется core.deltaBaseCacheLimit) автоматически и может значительно сократить количество "перестроек", необходимых для команды, которым необходимо прочитать большое количество объектов (например, git log -p).

Эвристика компрессии дельта

Типичный репозиторий Git хранит очень большое количество объектов, поэтому он не может разумно сравнить их все, чтобы найти пары (и цепей), которые будут давать наименьшие дельта-представления.

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

Для целей выбора дельта-базы объекты сортируются (в первую очередь) на имя файла, а затем размер. Окно в этот отсортированный список используется для ограничения количество объектов, которые рассматриваются как потенциальные базы дельта. Если для объекта не найдено "достаточно хорошее" 1 среди объектов в его окне, то объект не будет дельта сжатый.

Размер окна контролируется опцией --window= git pack-objects или переменной конфигурации pack.window. максимальная глубина дельта-цепи контролируется --depth= опции git pack-objects или конфигурации pack.depth переменная. Опция --aggressive git gc значительно расширяет как размер окна, так и максимальная глубина, чтобы попытаться создать файл меньшего пакета.

Сортировка имени файла объединяет объекты для записей с помощью идентичные имена (или, по меньшей мере, аналогичные окончания (например, .c)). Размер сортировка от самого большого до самого маленького, так что дельта, которая удаляет данные, предпочитают дельтам, которые добавляют данные (поскольку удаление дельтов короче представления) и так, что более ранние, более крупные объекты (обычно новее), как правило, представлены с простым сжатием.

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

Эквивалент объекта

Объекты хранятся в файлах пакетов в "последней ссылке", заказ. Объекты, необходимые для восстановления самой последней истории, размещенные ранее в пакете, и они будут близко друг к другу. Эта обычно хорошо работает для кэшей дисков ОС.

Все объекты фиксации сортируются по дате фиксации (последнее последнее) и хранятся вместе. Это размещение и заказ оптимизируют диск доступа, необходимые для прохождения графика истории и извлечения основного коммита информации (например, git log).

Объекты tree и blob хранятся, начиная с дерева из сначала сохраненный (последний) фиксация. Каждое дерево обрабатывается на глубине первая мода, хранение любых объектов, которые еще не были сохраняются. Это ставит все деревья и капли, необходимые для реконструкции последние совершают вместе в одном месте. Любые деревья и капли, которые еще не были сохранены, но которые необходимы для последующих совершений, являются сохраненный далее, в упорядоченном порядке фиксации.

Окончательное упорядочение объектов слегка зависит от выбора дельта-базы в том случае, если объект выбран для дельта-представления и его базового объекта еще не сохранен, тогда его базовый объект хранится непосредственно перед самого децитированного объекта. Это предотвращает появление ошибок в кэше диска из-за нелинейный доступ, необходимый для чтения базового объекта, который "естественно" был бы сохраненные позже в файле пакета.

Ответ 2

Использование дельта-хранилища в файле pack - это просто деталь реализации. На этом уровне Git не знает, почему и как что-то изменилось с одной ревизии на другую, а просто знает, что blob B очень похож на blob A, за исключением этих изменений C. Таким образом, он будет хранить только blob A и изменения C (если он захочет сделать это - он также может выбрать сохранение blob A и blob B).

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

Ответ 3

Как я упоминал в "Что такое тонкие пакеты git?"

Git делает делит только в packfiles

Я подробно описал дельта-кодировку, используемую для файлов пакетов, в Стандартизирован ли бинарный алгоритм Git (дельта-хранилище)?".
См. Также "Когда и как Git используют дельта для хранения?".

Обратите внимание, что конфигурация core.deltaBaseCacheLimit, которая управляет размером по умолчанию для файла пакета, скоро будет перегружена с 16 МБ до 96 МБ, для Git 2.0.x/2.1 (Q3 2014).

См. передать 4874f54 Дэвидом Каструпом (май 2014 г.):

Bump core.deltaBaseCacheLimit до 96 м

Значение по умолчанию 16 м приводит к серьезному обману для больших дельта-цепей в сочетании с большими файлами.

Вот несколько тестов (вариант pu для git wame):

  время Git винить -C src/xdisp.c >/dev/null
Код>

для репозитория Emacs, переупакованного с помощью git gc --aggressive (v1.9, в результате чего размер окна равен 250), расположенный на диске SSD.
Этот файл имеет около 30000 строк, размер 1 Мбайт и историю с 2500 коммитов.

  16 м (предыдущее значение по умолчанию): реальный 3m33.936s пользователь 2m15.396s sys 1m17.352s

96m: реальный 2m5.668s пользователь 1m50.784s sys 0m14.288s
Код>