Git LFS: как хранить большие файлы в Git-репозитории
Git LFS (Large File Storage) — расширение Git, которое хранит большие бинарные файлы на отдельном сервере, а в репозиторий кладёт только маленькие файлы-указатели. Клонирование становится быстрым, история не раздувается, а работа с файлами остаётся привычной: git add, commit и push.
Что такое Git LFS и зачем он нужен
Git LFS (Large File Storage) — официальное расширение Git для хранения больших файлов: видео, макетов PSD, датасетов, моделей нейросетей, игровых ассетов. Вместо самого файла в коммит попадает текстовый указатель размером около 130 байт, а содержимое уходит в отдельное LFS-хранилище на сервере. При checkout расширение подставляет настоящий файл в рабочую директорию, поэтому для вас всё выглядит как обычный Git.
Проблема, которую решает git lfs, в устройстве самого Git. Каждая версия каждого файла навсегда остаётся в истории, и при клонировании скачивается вся история целиком. Если вы десять раз поменяли видеоролик на 200 МБ, репозиторий весит уже около 2 ГБ, и каждый новый разработчик скачивает все десять версий, даже если ему нужна только последняя. Сжатие и дельты, которые хорошо работают для текста, на бинарных файлах почти бесполезны.
С LFS при клонировании скачиваются только версии больших файлов для текущего коммита. Старые версии лежат на сервере и подтягиваются, когда вы переключаетесь на старый коммит.
Как работает Git LFS изнутри
Механизм построен на фильтрах Git — clean и smudge. Когда вы делаете git add файла, который отслеживает LFS, срабатывает фильтр clean: он кладёт содержимое в локальный кэш .git/lfs/objects и передаёт в индекс указатель. При checkout фильтр smudge делает обратное: находит объект по хешу в кэше (или скачивает с сервера) и записывает настоящий файл.
Указатель выглядит так:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 209715200
Три строки: версия спецификации, SHA-256 содержимого и размер в байтах. Именно это хранится в истории Git, поэтому история остаётся лёгкой. При git push срабатывает pre-push хук: сначала по LFS API загружаются новые объекты, затем обычным протоколом отправляются коммиты.
Какие файлы отдавать LFS, определяет файл .gitattributes в корне репозитория. Он коммитится вместе с кодом, поэтому правила одинаковы у всей команды.
Как установить Git LFS
В Git для Windows расширение уже входит в установщик. На macOS и Linux его ставят пакетом:
# macOS (Homebrew)
brew install git-lfs
# Debian / Ubuntu
sudo apt install git-lfs
# Fedora
sudo dnf install git-lfs
После установки один раз для пользователя выполните:
git lfs install
Команда прописывает фильтры LFS в глобальный ~/.gitconfig и ставит хуки в текущий репозиторий, если вы внутри него. Проверить, что всё на месте, можно командой git lfs version, а подробную конфигурацию (адрес сервера, пути кэша) показывает git lfs env.
Как начать отслеживать большие файлы
Типичный сценарий для нового проекта:
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "models/**"
git add .gitattributes
git add design/banner.psd
git commit -m "Добавить макет баннера через LFS"
git push origin main
Кавычки вокруг шаблона обязательны: без них оболочка сама раскроет *.psd в список файлов текущей папки, и правило получится не тем. После git lfs track в .gitattributes появится строка:
*.psd filter=lfs diff=lfs merge=lfs -text
Атрибут -text отключает преобразование переводов строк, которое испортило бы бинарный файл. Порядок важен: сначала правило track, потом git add файла. Если файл уже был добавлен обычным образом до появления правила, он остался в истории как обычный blob.
Как проверить, что файл действительно в LFS
git lfs ls-files
4d7a214614 * design/banner.psd
9c1e0b7a3f * video/intro.mp4
git lfs status
git lfs ls-files показывает файлы текущего коммита, хранящиеся в LFS. Звёздочка означает, что содержимое скачано, дефис — что в рабочей копии лежит только указатель. Список шаблонов отслеживания выводит git lfs track без аргументов, а убрать правило можно через git lfs untrack "*.psd".
Клонирование и получение файлов
Обычный git clone работает с LFS прозрачно: если расширение установлено, большие файлы скачиваются автоматически. Если же у коллеги LFS не установлен, в рабочей копии он увидит текстовые указатели вместо картинок — это самый частый симптом «у меня вместо PSD какой-то текст». Лечится установкой и командой:
git lfs install
git lfs pull
git lfs pull скачивает объекты для текущего коммита и подставляет их в рабочую директорию. Отдельно существует git lfs fetch — он только качает в кэш, не трогая файлы. С флагом --all он загружает объекты для всех ссылок, что пригодится для полного бэкапа или переноса на другой сервер.
Как клонировать без больших файлов
В CI или когда нужен только код, скачивание гигабайт ассетов лишнее. Переменная окружения отключает smudge-фильтр:
GIT_LFS_SKIP_SMUDGE=1 git clone https://example.com/team/game.git
cd game
git lfs pull --include="textures/ui/**"
Репозиторий клонируется с указателями, а затем вы выборочно забираете нужные файлы через --include (есть и --exclude). Это заметно ускоряет сборки в пайплайнах.
Как перенести существующие файлы в Git LFS
Частая ситуация: репозиторий уже раздулся, большие файлы лежат в истории. Команда git lfs track на прошлое не действует — она влияет только на новые коммиты. Чтобы переписать историю, используйте git lfs migrate.
Сначала оцените, что занимает место:
git lfs migrate info --everything --top=10
Затем перенесите нужные типы во всех ветках:
git lfs migrate import --include="*.psd,*.mp4" --everything
git push --force --all
git push --force --tags
Миграция переписывает каждый коммит, где встречались эти файлы, значит меняются все хеши. Это та же история, что с rebase опубликованных веток: всех участников нужно предупредить, после принудительного push они должны заново клонировать репозиторий или жёстко сбросить свои ветки. Перед миграцией сделайте резервную копию (git clone --mirror).
Если переписывать историю нельзя, есть мягкий вариант: git lfs migrate import --no-rewrite путь/к/файлу. Он создаёт новый коммит, который переводит файлы в LFS с этого момента. Старые версии останутся в истории, но рост репозитория прекратится.
Блокировка файлов: защита от конфликтов в бинарниках
Два дизайнера правят один PSD — и Git не сможет слить их изменения, одна работа будет потеряна. Для таких файлов в LFS есть блокировки. Помечаете тип как блокируемый:
git lfs track "*.psd" --lockable
В .gitattributes добавится атрибут lockable, и такие файлы в рабочей копии станут доступны только для чтения, пока вы их не заблокируете:
git lfs lock design/banner.psd
git lfs locks
design/banner.psd anna ID:123
# после push изменений
git lfs unlock design/banner.psd
Пока файл заблокирован, push изменений в него от другого человека будет отклонён. Блокировки поддерживает сервер, поэтому они работают на крупных Git-хостингах, но на самописном сервере их может не быть. Администратор может снять чужую блокировку через git lfs unlock --force.
Где хранятся LFS-объекты и сколько это стоит
LFS-объекты живут на сервере, который поддерживает LFS API. Его реализуют популярные Git-хостинги и self-hosted решения, а также отдельные серверы. У облачных хостингов есть лимиты на объём хранилища и трафик LFS, и они отличаются у разных сервисов и тарифов — проверяйте актуальные условия своей платформы до того, как класть туда датасеты на сотни гигабайт.
Учтите, что трафик считается при каждом скачивании: CI, который на каждый коммит клонирует репозиторий с LFS-файлами, может выбрать лимит быстрее, чем люди. Кэшируйте .git/lfs между сборками или используйте GIT_LFS_SKIP_SMUDGE.
Локально кэш тоже растёт. Команда git lfs prune удаляет старые объекты, которые уже есть на сервере и не нужны для текущих и недавних коммитов.
Когда Git LFS подходит, а когда нет
| Ситуация | LFS подходит? | Комментарий |
|---|---|---|
| Игровые ассеты, макеты, аудио, видео | Да | Классический случай: бинарники, которые часто меняются |
| Модели ML и средние датасеты рядом с кодом | Да, с оглядкой на квоты | Для очень больших данных удобнее специализированные инструменты версионирования данных |
| Скомпилированные артефакты сборки | Нет | Их место в реестре артефактов или релизах, а не в Git |
| Зависимости (node_modules, vendor) | Нет | Устанавливаются менеджером пакетов, добавьте их в .gitignore |
| Большие текстовые файлы (SQL-дампы, CSV) | Зависит | Текст Git хранит дельтами эффективно; LFS нужен, если файлы огромные и почти не читаются в diff |
| Секреты и приватные ключи | Никогда | LFS не шифрует данные, это не хранилище секретов |
Типичные ошибки и как их исправить
В рабочей копии вместо файла текстовый указатель
LFS не установлен или клон сделан с GIT_LFS_SKIP_SMUDGE=1. Выполните git lfs install и git lfs pull.
Файл попал в историю мимо LFS
Правило track добавили после git add. Если коммит ещё не отправлен, уберите файл из индекса (git rm --cached файл), убедитесь, что правило в .gitattributes есть, и добавьте заново. Если уже отправлен — git lfs migrate import. Найти такие файлы помогает git lfs fsck --pointers.
Push отклоняется из-за размера файла
Многие хостинги ограничивают размер одного объекта в обычном Git. Сообщение вроде «this exceeds the file size limit» значит, что файл надо было отдать LFS. Перепишите последний коммит или историю, как описано выше, — простой коммит «удалить файл» не поможет, он останется в предыдущих коммитах.
После git lfs migrate у коллег всё сломалось
Их локальные ветки основаны на старых хешах. Надёжнее всего заново клонировать репозиторий; при наличии незапушенной работы — перенести её через cherry-pick на новые ветки.
Шпаргалка команд Git LFS
| Команда | Что делает |
|---|---|
git lfs install | Настраивает фильтры и хуки |
git lfs track "*.ext" | Добавляет шаблон в .gitattributes |
git lfs untrack "*.ext" | Убирает шаблон |
git lfs ls-files | Файлы в LFS для текущего коммита |
git lfs pull | Скачать и подставить объекты |
git lfs fetch --all | Скачать объекты всех ссылок в кэш |
git lfs migrate import --include="..." --everything | Перенести файлы из истории в LFS |
git lfs lock / unlock / locks | Работа с блокировками |
git lfs prune | Очистить локальный кэш от старых объектов |
git lfs env | Диагностика настроек |
Итог
Git LFS выносит большие бинарные файлы из истории в отдельное хранилище, оставляя в коммитах лёгкие указатели. Главное правило — настраивать git lfs track до первого коммита больших файлов, коммитить .gitattributes и следить за квотами хостинга. Если файлы уже в истории, поможет git lfs migrate, но это переписывание истории со всеми последствиями. Работу с историей, rebase и другими продвинутыми инструментами Git мы разбираем на практике в курсе по Git.
Частые вопросы
Git LFS бесплатный?
Само расширение — открытое и бесплатное. Платить можно за хранилище и трафик у Git-хостинга, если превысить бесплатные лимиты вашего тарифа.
Какой размер файла считать большим для LFS?
Жёсткой границы нет; обычно в LFS отправляют бинарники от нескольких мегабайт, которые часто меняются. Текстовые файлы даже крупного размера Git хранит эффективно и без LFS.
Можно ли отказаться от Git LFS и вернуть файлы в обычный Git?
Да, командой git lfs migrate export --include="*.psd" --everything, которая переписывает историю обратно. После этого нужно убрать правила из .gitattributes и сделать force push.
Работает ли git diff для файлов в LFS?
Git покажет изменение указателя, а не содержимого, потому что файлы бинарные. Для картинок и документов сравнение делают внешними инструментами.
Нужен ли LFS каждому участнику команды?
Да, иначе у участника без расширения в рабочей копии будут указатели вместо файлов, а его коммиты могут случайно добавить большие файлы мимо LFS.
Эта тема подробно разобрана с практикой в курсе «Git с нуля до уверенного разработчика».
Перейти к курсу