Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як влаштовані packfile і дельта-стиснення, якщо Git зберігає повні знімки?

Логічно кожна версія файлу - окремий blob з повним вмістом. Новий об'єкт спершу записується як loose object - окремий файл у .git/objects/xx/, стиснений zlib. Для файлу на 20 КБ, зміненого в 100 комітах, це 100 майже однакових копій.

Packfile вирішує це на рівні зберігання. git gc (і передача по мережі) збирає об'єкти в один файл:

.git/objects/pack/
├── pack-4f2a....pack   ← самі об'єкти
├── pack-4f2a....idx    ← індекс: хеш → зміщення в pack
├── pack-4f2a....rev    ← зворотний індекс (зміщення → позиція)
└── pack-4f2a....bitmap ← бітмапи досяжності (для швидкого clone/fetch)

Дельта-стиснення: частина об'єктів зберігається не повністю, а як дельта від іншого об'єкта - інструкції «скопіювати байти з базового об'єкта» й «вставити нові байти».

git verify-pack -v .git/objects/pack/pack-*.idx | sort -k3 -n | tail
# хеш   тип   розмір  розмір-у-pack  зміщення  [глибина  база]

Особливості, що відрізняють Git від «diff між версіями»:

  • база обирається евристично, а не за історією: Git сортує об'єкти за типом, ім'ям файлу й розміром і шукає схожі у ковзному вікні. Дельта може будуватися від файлу з іншої гілки чи навіть з іншим ім'ям;
  • зазвичай база - новіша версія, а старі зберігаються як дельти від неї: свіжі файли, які читають найчастіше, дістаються без розпакування ланцюжка;
  • ланцюжки дельт обмежені глибиною (pack.depth, за замовчуванням 50) - баланс між розміром і швидкістю читання;
  • логічна модель не змінюється: для всіх команд об'єкт - повний знімок, дельти - лише деталь зберігання.

Передача по мережі: при fetch і push сторони узгоджують, які коміти вже є в отримувача, і сервер формує тонкий pack (thin pack) - дельти можуть посилатися на об'єкти, що вже є в клієнта. Тому fetch після невеликих змін передає кілобайти.

Практичні наслідки:

  • бінарні файли (зображення, архіви, дампи) погано стискаються дельтами - кожна версія займає майже повний розмір, і репозиторій росте назавжди. Звідси Git LFS;
  • git gc --aggressive перебудовує дельти з більшим вікном - повільно, і для більшості репозиторіїв виграш мізерний;
  • git count-objects -vH показує, скільки займають loose objects і pack-и: велика кількість loose objects - сигнал, що автоматичне обслуговування не запускається.

Докладніше в документації: Pro Git: packfiles

Схожі питання