Являются ли файлы пакета Git дельтами, а не снимками? - PullRequest
63 голосов
/ 03 марта 2011

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

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

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

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

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

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

Ответы [ 3 ]

67 голосов
/ 07 апреля 2011

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


Git's pack file формат довольно гибкий (см. Documentation / technical / pack-format.txt , или Packfile в The Git Community Book ). Файлы пакета хранят объекты в двух основных способы: «не определено» (взять необработанные данные объекта и сжать-сжать) это), или «delified» (образуют дельту против какого-то другого объекта, то дефляция-сжатие результирующих дельта-данных). Объекты, хранящиеся в пачка может быть в любом порядке (они не обязательно должны быть) отсортированы по типу объекта, имени объекта или любому другому атрибуту) и Различающиеся объекты могут быть сделаны против любого другого подходящего объекта того же типа.

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

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

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

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

7 голосов
/ 03 марта 2011

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

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

4 голосов
/ 27 июля 2014

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

Git выполняет делитацию только в пакетных файлах

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

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

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

Увеличить core.deltaBaseCacheLimit до 96 м

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

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

time git blame -C src/xdisp.c >/dev/null

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

16m (previous default):
  real  3m33.936s
  user  2m15.396s
  sys   1m17.352s

96m:
  real  2m5.668s
  user  1m50.784s
  sys   0m14.288s
...