Насколько я понимаю, свободные объекты, хранящиеся в [голом] хранилище git, сжаты ...
Они есть. Но они zlib-deflate сжаты.
... так почему у объектов git pack (и всех связанных с ними команд repack и gc) действительно длинный этап Compressing objects?
Эти команды - git pack-objects
и git repack
, в любом случае; git gc
просто запускает git repack
для вас - объедините множество объектов в один файл пакета .
Файл пакета - это другой способ сжатия объектов. Свободный объект является автономным: Git нужно только прочитать свободный объект и выполнить над ним проход zlib inflate, чтобы получить несжатые данные для этого объекта. Файл пакета, напротив, содержит много объектов, причем некоторые из этих объектов, во-первых, с дельта-сжатием .
Дельта-сжатие работает, говоря, по сути: Чтобы создать этот объект, сначала создайте этот другой объект. Затем добавьте эти байты сюда и / или удалите N байтов здесь. Повторяйте это добавление и / или удаление до тех пор, пока я не закончу со списком дельт. (Сами инструкции дельты также могут быть дефлированы zlib.) Вы можете распознать это как своего рода diff, и на самом деле, некоторые системы контроля версий, не относящиеся к Git, действительно используют diff или свой собственный внутренний механизм сравнения для создания своих файлов, сжатых дельтой.
Традиционно для этого используется наблюдение, что некоторый файл (скажем, foo.cc
или foo.py
) имеет тенденцию изменяться с течением времени, добавляя и / или удаляя несколько строк где-то в файле, но сохраняя большую часть его неизменным. , Если мы можем сказать: взять все предыдущие версии, но затем добавить и / или удалить эти строки , мы можем хранить обе версии на гораздо меньшем пространстве, чем требуется для хранения одной из них.
Конечно, мы можем построить дельта-сжатый файл поверх предыдущего дельта-сжатого файла: Возьмите результат расширения предыдущего дельта-сжатого файла и примените эти дельты. Эти составляют дельта-цепочки , которые могут быть сколько угодно, возможно, вплоть до момента, когда файл был впервые создан.
Некоторые (не Git) системы останавливаются здесь: каждый файл сохраняется либо как изменение предыдущей версии, либо, каждый раз, когда вы сохраняете файл, система сохраняет последнюю версию и поворачивает предыдущую полную копию (которая использовалась чтобы быть последним, и, следовательно, было полной копией) в дельту, необходимую для преобразования «последнего» в «предыдущий». Первый метод называется прямое дельта-хранилище , а второй, конечно, обратное дельта-хранилище . Прямые дельты, как правило, имеют ужасный недостаток, поскольку для извлечения последней версии файла требуется извлечь первую версию, а затем применить очень длинную последовательность дельт, что занимает много времени. Поэтому RCS использует обратные дельты, что означает быстрое получение последней версии; он получает очень старую версию, которая работает медленно. (Однако по техническим причинам это работает только для того, что RCS называет trunk . Вместо этого в «ветвях» RCS используются прямые дельты.) Mercurial использует прямые дельты, но иногда сохраняет новую полную копию файла, поэтому чтобы длина цепи дельта была короткой. Одна система, SCCS, использует метод, который SCCS вызывает с перемежением дельт , который дает линейное время для извлечения любого файла (но его сложнее генерировать).
Git, однако, не сохраняет файлы как files . Вы уже знаете, что данные файла хранятся как объект blob , который изначально просто zlib-дефлирован, а в остальном не поврежден. Учитывая набор объектов, некоторые из которых являются данными файла, а некоторые нет (являются коммитами, деревьями или аннотированными объектами тегов), совершенно не очевидно, какие данные принадлежат какому файлу (файлам). Поэтому Git находит вероятного кандидата: некоторый объект, который, похоже, очень напоминает какой-то другой объект, лучше всего выразить словами: начните с другого объекта, затем внесите эти дельта-изменения.
Большая часть процессорного времени, затрачиваемого на сжатие, заключается в поиске хороших цепочек. Если система контроля версий плохо выбирает файлы (или объекты), сжатие будет не очень хорошим. Git использует кучу эвристик, в том числе заглядывает в древовидные объекты, чтобы восстановить имена файлов (только базовые имена, а не полные пути), так как в противном случае сложность времени действительно сводится с ума. Но даже с эвристикой, поиск хороших цепей дельта дорог. Точность настройки настраивается с помощью настроек «окна» и «глубины».
Более подробную информацию о пакетных файлах, которые со временем претерпели несколько изменений, смотрите в каталоге Documentation / technical в Git .