GitLFS: Объемные файлы в Git-репозитории
История Git LFS началась в 2014-м году, когда в разработчики GitHub столкнулись с тем, что некоторые пользователи кладут в репозиторий огромные файлы и потом начинают жаловаться на проблемы с производительностью репозитория😒. Эта история типична для команд, которые активно работают с медиа файлами, с CAD программами и которым хочется версионировать их также как и остальные файлы. Однако ванильный Git плохо работает с большими бинарниками по нескольким причинам:
- Операция git status сканирует все файлы в директории и для каждого вычисляет хеш. Чем больше файлы, тем сложнее эта операция.
- Даже при небольших изменениях бинарные файлы, как правило, меняются сильно. В результате delta-сжатие работает плохо, и размер репозитория растет как на дрожжах. В результате очень быстро репозиторий доходит до состояния, когда git clone может выполняться часами.
Чтобы побороть эту проблему инженеры GitHub приняли очень простое и логичное решение - хранить такие файлы отдельно в хранилище а-ля S3 и предоставить отдельный API для работы с ними. В результате родилось расширение для Git с названием Git LFS (Git Large File System), которое сразу было сделано открытым стандартом, в результате сейчас Git LFS поддерживается всеми современными git сервисами (GitHub, GitLab, Bitbucket, GitVerse и т. д.)
Как работает под капотом
Для того, чтобы все сработало, на локальную машину нужно установить отдельную утилиту Git LFS. После установки и выполнения например следующей команды
git lfs track "*.psd"
git lfs будет встраиваться в процесс git add, git checkout, git push, git pull.
Так например, для git add
psd файла содержимое файла будет заменено на так называемый файл-указатель следующего вида:
version https://git-lfs.github.com/spec/v1
oid sha256:abcdef123456...
size 2048576
где указаны версия протокола, sha256 хеш и размер оригинального файла. И уже этот маленький файл попадет в индекс git.
Когда выполняется git push
для этого файла, то оригинальный файл будет отправлен на Git LFS сервер, и только если эта операция прошла успешно, то в основной репозиторий будет отправлен файл-указатель.
Когда выполняется git checkout
, то LFS-фильтр получает на вход файл-указатель из индекса и скачивает из LFS-хранилище реальный файл по его хешу.
Аналогично, когда выполняется git clone
, то скачивается индекс с полной историей коммитов, где есть только работа с файлами-указателями, а с LFS-хранилища скачиваются только те версии больших файлов, на которые есть ссылки в последней версии изменений.
Кстати, если ваш коллега забудет поставить git LFS на своей машине, то когда он скачает репозиторий, то вместо оригинальных файлов будет видеть файлы-указатели :)
Когда применять
Если вы добавили в репозиторий несколько бинарников на 10 Мб, то спешить включать Git LFS может и не стоит, но если в репозитории завелись бинарные файлы размером больше 1Мб, и если они регулярно добавляются/меняются/удаляются, то это явный сигнал, что надо включать Git LFS.
Если файлы сверх большие (порядка сотен Мб), хоть и не меняются, то лучше всего вынести их во внешнее хранилище, смысла клать их в репозиторий особого нет. Но если очень хочетя, то тогда сразу с включенным Git LFS.
Если файлы бинарные, но не большие (килобайты), например картинки на сайте, то LFS не нужен, ванильный git отлично с работает с такими файлами.