Настройка Git LFS: хранение больших файлов и ускорение CI/CD

Репозиторий растёт, сборки буксуют, а выгрузка занимает часы? Мы настраиваем Git LFS под ключ: выносим большие файлы из репозитория, ускоряем сборки и передаём команде понятные регламенты работы. Время ожидания сборки сокращается в разы, а история проекта остаётся целой.
Часто задаваемые вопросы

Почему Git LFS нужен: репозиторий растёт, сборки замедляются

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

Дальше включается каскад. Клонирование репозитория растягивается на десятки минут, потому что клиент выкачивает все версии файла, включая давно не нужные. Сборочный агент тратит больше времени на разогрев рабочей копии, чем на саму сборку, параллельные задания упираются в диск и сеть, а кэш постоянно сбрасывается при переполнении. Чем активнее команда, тем сильнее раздувается история — и тем дороже каждый следующий прогон.

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

Git LFS меняет саму модель хранения: крупные объекты уезжают в отдельное хранилище, а в репозитории остаются указатели — они весят байты и не растут от версии к версии. Разбираем, как это закрывается на практике: от выбора места хранения и миграции уже накопленной истории до правил, которые не дают новым бинарным артефактам просачиваться мимо LFS.

Что меняется после выноса больших файлов из репозитория

Большие файлы — бинарные сборки, медиа, макеты, веса моделей — держат историю репозитория в заложниках: каждая правка добавляет новую полную копию объекта, а предыдущие остаются в истории навсегда. В Git LFS содержимое заменяется указателем, а сами данные уезжают в отдельное объектное хранилище. Репозиторий снова хранит текстовые изменения, а не артефакты.

Первый измеримый эффект — время клонирования. Разработчик и сборочная машина вытягивают только актуальные версии крупных файлов, а не всю их историю. Рабочая копия перестаёт распухать: вместо гигабайтов бинарников — килобайты указателей, переключение между ветками укладывается в секунды даже на узком канале.

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

Стабильность сборки здесь — не про скорость, а про предсказуемость: задача падает на ошибке в коде, а не на инфраструктуре. Плата за это — дисциплина окружения: клиент LFS в образе сборочной машины, правила хранения и резервные копии объектного хранилища. История при этом сохраняется целиком — откат на любой прошлый релиз остаётся возможным.

Варианты хранения больших файлов: сравнение подходов

Выбор способа хранения больших файлов определяет, как быстро клонируется репозиторий, сколько занимает история и что произойдёт, если внешняя служба окажется недоступна. Ниже — четыре варианта, между которыми реально выбирают на проектах.

Вариант Когда подходит Ограничения
Обычное хранение в репозитории Небольшие бинарники, которые нужны всей команде и меняются редко Каждая версия остаётся в истории навсегда: клон растёт вместе с числом правок, а не с размером файла
Выделенное хранилище указателей Тяжёлые файлы в привычном потоке работы: графика, медиа, тестовые наборы данных Нужен настроенный сервер и правило очистки; без него объём хранилища растёт бесконтрольно
Внешнее файловое хранилище Большие объёмы, разграничение доступа, отдача данных по сети Репозиторий и сборка зависят от чужой доступности и сетевого канала
Локальное хранение Черновые материалы, которым не место в общем доступе Нет воспроизводимости: другой инженер или сборочный агент не получит те же данные

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

Как проходит внедрение: от аудита до передачи регламентов

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

  1. Аудит репозитория. Считаем объём и историю: какие расширения дают основной вес, сколько ревизий затронуто, кто и как часто коммитит крупные файлы. На выходе — перечень путей, которые переводим на внешнее хранилище, и оценка размера после миграции.
  2. План миграции. Определяем, переписываем ли историю целиком или переводим только новые коммиты, согласуем окно, когда правки в основную ветку приостановлены. Фиксируем порядок отката, если проверка не пройдёт.
  3. Настройка хранилища. Разворачиваем или подключаем объектное хранилище, задаём правила хранения: срок жизни старых версий, лимиты на размер объекта, разграничение доступа по ролям. Прописываем фильтры и правила в конфигурации репозитория.
  4. Перевод команд и сборки. Обновляем настройки у разработчиков и на агентах сборки, чтобы тяжёлые файлы скачивались по требованию, а не в каждом клоне. Для репозитория на десятки гигабайт это срезает время получения рабочей копии в разы.
  5. Проверка целостности. Сверяем контрольные суммы объектов до и после миграции, собираем проект с нуля на чистом клоне, проверяем доступность старых ревизий и тегов. Только после этого закрываем окно миграции.
  6. Передача в эксплуатацию. Отдаём регламенты и инструкции, настраиваем оповещения о росте хранилища, проводим обучение команды и фиксируем ответственного за правила.

Правила указателей, доступы и документация проекта

Внедрение считается завершённым не тогда, когда большие файлы перестали тормозить клонирование, а когда у команды есть набор артефактов, позволяющий менять правила без нас. Ниже — то, что заказчик получает на выходе и что мы разбираем с его инженерами на передаче.

  • Файл правил сопоставления — какие расширения и пути уходят в LFS, а какие остаются в обычном индексе. Новая категория бинарных файлов добавляется правкой одной строки, а не пересборкой репозитория.
  • Конфигурация указателей и хранилища — адрес объектного хранилища, режим блокировки «несливаемых» файлов, ограничения на параллельные передачи. Одна и та же настройка на рабочих машинах и на сборочных агентах, чтобы поведение не расходилось.
  • Схема хранения — где физически лежат объекты, как устроен кэш на агентах, какие правила очистки действуют: сколько ревизий держим, что удаляется первым, как восстанавливается объект после ошибочного удаления.
  • Политики доступа — какие учётные записи и токены могут читать и записывать объекты, как выдаются права подрядчикам и внешним командам, где хранятся секреты сборки и как они ротируются.
  • Инструкции для команды — как правильно клонировать репозиторий с большими файлами, что делать при обрыве передачи, как освободить место в рабочей копии, как поставить блокировку на файл, который нельзя сливать автоматически.
  • Регламент работы — порядок правки файла правил: кто согласует перевод новой категории файлов в LFS, как проверяется, что указатель не остался без своего объекта, кто отвечает за рост хранилища.
  • Проверки в сборке — автоматический контроль «указатель без объекта» и лимита размера, чтобы проблема обнаруживалась на этапе сборки, а не при клонировании у нового разработчика.

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

Кейс: как разгрузили репозиторий и ускорили сборку

Репозиторий продукта рос не за счёт кода: рядом с исходниками лежали макеты интерфейса, тестовые видеозаписи и наборы данных для проверки выгрузок. Клонирование на чистой машине занимало около 40 минут, рабочая копия была в четырнадцать раз тяжелее самого кода, а из 30 минут сборки 12 уходили на выкачивание истории. Выгрузки падали примерно в каждой пятой попытке — обычно на превышении времени ожидания при получении вложений.

Разделили потоки: код остался в основной истории, тяжёлые файлы перевели на Git LFS с правилами в .gitattributes по маскам типов, а редко меняющиеся тестовые наборы вынесли в отдельное хранилище с версионированием по ссылке. В образ сборщика добавили предзагрузку нужных объектов, чтобы шаги не тянули их по одному через сеть, ограничили права записи и включили проверку на файлы сверх порога — коммит больше не может незаметно утяжелить историю.

После переноса клонирование уложилось в 4 минуты, сборка сократилась до 11, рабочая копия у разработчика стала меньше в четырнадцать раз. Падений на выгрузке за квартал — единицы вместо каждого пятого запуска, доля неуспешных доставок упала ниже одного процента. Репозиторий теперь растёт предсказуемо: политика хранения ограничивает размер служебного хранилища, а откат на предыдущую версию ассетов остаётся доступным.

Чем Git LFS отличается от обычного хранения в репозитории

При обычном хранении бинарный артефакт — образ, датасет, видео — коммитится в ту же базу объектов, что и код. Дельта-сжатие, которое держит историю текстовых файлов компактной, на бинарниках не работает: каждая версия ложится полной копией. Удаление из текущей ветки ничего не освобождает — объект остаётся в истории, и клон тянет все его ревизии.

Git LFS меняет схему: в репозиторий попадает текстовый указатель — тип объекта, хеш и размер, — а сами байты уезжают в отдельное хранилище. История коммитов, ветки и теги остаются на месте, но дерево версий весит столько, сколько занимают строки указателей. Разница подходов проявляется не в командах разработчика, а в том, что именно тянется по сети при клоне и сборке.

Восстановление рабочего дерева тоже различается. Обычный клон выкачивает всю историю больших файлов, включая версии, удалённые много релизов назад; режим указателей отдаёт только нужные текущему дереву объекты. Плата — сетевая зависимость: без доступа к хранилищу на месте файлов останутся текстовые заглушки, а фильтры нужно настроить до первого клона.

При ветвлении одинаковые файлы дедуплицируются по хешу: одна копия в релизной и рабочей ветке хранится один раз, тогда как в обычной схеме объект живёт во всех ветках и замедляет обход истории. Ограничения хранения при этом не исчезают, а переезжают: появляется отдельный сервер, квоты по объёму и трафику, своя политика жизненного цикла и бэкап, не связанный с репозиторием. В конвейере сборки это даёт выборочную загрузку нужных путей вместо полной истории — подготовка окружения ускоряется, но шаг загрузки объектов настраивается явно, иначе сборка молча получит указатели вместо файлов.

Сомнения по хранению, доступам и восстановлению файлов

Внедрение LFS редко заканчивается одной командой отслеживания. В истории уже лежат бинарные объекты обычными файлами, поэтому нужен план переноса: без него свежий клон тянет старые ветки целиком.

Вторая группа сомнений — эксплуатация: где физически лежат объекты и кто к ним имеет доступ. Отдельно проверяем доступность хранилища в момент сборки: сборочный агент либо останавливается с явной ошибкой, либо берёт объекты из локального кэша — это задаётся заранее, а не в момент сбоя.

Что будет со старыми ветками и историей?

Старые ветки переводим отдельным проходом: объекты выносятся из истории, на их место встают указатели из .gitattributes. Неподдерживаемые ветки обычно закрываем и оставляем архивом — переписывать их дороже, чем польза.

Как дать доступ внешним подрядчикам?

Подрядчику выдаём отдельную учётную запись: только чтение и только нужное пространство объектов. Он забирает большие файлы, но не может перезаписать чужие, а доступ отзывается после сдачи работ.

Как восстанавливать большие файлы из резервной копии?

Копия репозитория бесполезна — в ней указатели, а не сами данные. В регламент добавляем бэкап хранилища объектов, а восстановление проверяем на тестовом клоне, чтобы неполную копию не обнаружить в момент аварии.

Обсудим задачу: схема хранения и порядок миграции

На обсуждение заказчик приносит список репозиториев и понимание, какие файлы туда попадают: бинарные сборки, модели, медиа, выгрузки из внешних систем. Полезны текущие правила трекинга, размер рабочей копии у разработчиков и жалобы на время клонирования или сборки — по ним сразу видно, где узкое место и что именно ускоряем.

Из этого собирается план работ: схема хранения (где размещается LFS-хранилище, какие маски файлов уходят в трекинг, какие лимиты ставим), порядок внедрения по репозиториям и оценка сроков. Массовой миграцией не начинаем — сначала пилот на одном репозитории, чтобы проверить клон, сборку и откат на живом конвейере.

  • инвентаризация репозиториев и файлов, которые переводятся в LFS;
  • схема хранения: размещение хранилища, правила трекинга по маскам, лимиты на размер;
  • порядок внедрения: пилотный репозиторий, затем остальные волнами;
  • миграция истории и очистка уже закоммиченных крупных объектов;
  • правки конвейера сборки, кэша зависимостей и правил доступа для агентов;
  • роли и права: кто читает и пишет в хранилище, как выдаются ключи;
  • план отката, если клон или сборка деградируют после перехода.

Возвращаемся с оценкой сроков и объёма работ: этапы, что делаем мы, что нужно со стороны заказчика, критерии приёмки — время клонирования, размер рабочей копии, длительность сборки. Опишите задачу — вернёмся с оценкой и планом.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139