Устранение проблем с производительностью, вызванных большими файлами в Git
Диагностика медленных репозиториев Git, вызванных большими файлами, осторожный выбор Git LFS и очистка истории без неожиданностей для вашей команды.
Устранение проблем с производительностью, вызванных большими файлами в Git
Большие файлы наносят вред Git таким образом, что это легко не заметить, пока репозиторий не станет болезненно медленным в использовании. Один видеофайл, архив, дамп базы данных или дизайн-файл может не выглядеть опасным, когда кто-то его добавляет. Проблемы начинаются, когда этот файл изменяется несколько раз. Git хранит историю, и каждый клон вынужден таскать эту историю за собой.
Симптом обычно сначала неясен. git clone занимает больше времени, чем должен. git fetch кажется медленным на Wi-Fi в отеле. CI-задачи тратят слишком много времени на проверку репозитория, прежде чем начать сборку. Разработчики начинают использовать старые локальные клоны, потому что свежий клон раздражает. Это момент, когда нужно проверить репозиторий, а не говорить всем быть терпеливыми.
Подтвердите, что проблема в больших файлах
Начните с простых проверок:
du -sh .git
git count-objects -vH
du -sh .git показывает, насколько тяжела локальная база данных репозитория. git count-objects -vH показывает количество рыхлых объектов и размер пакета. Если размер пакета велик по сравнению с фактическим деревом исходного кода, история, вероятно, несет старые полезные нагрузки.
Чтобы найти большие файлы в текущем checkout:
find . -path ./.git -prune -o -type f -size +10M -print
Это показывает только то, что существует сейчас. Репозиторий может быть медленным из-за файла, который был удален месяцы назад. Чтобы проверить историю, Git LFS предоставляет полезный отчет еще до того, как вы что-то мигрируете:
git lfs migrate info --everything --above=10MB
Если Git LFS не установлен, вы все равно можете исследовать с помощью plumbing Git, но вышеуказанная команда часто является наиболее прямым способом для этой конкретной проблемы.
Решите, что должно быть в Git
Не каждый большой файл — это ошибка. Небольшой набор стабильных бинарных ресурсов может быть нормальным. Репозиторий для инфраструктурного кода не должен содержать образы виртуальных машин, резервные копии баз данных, экспорт клиентов или артефакты сборки. Репозиторий игры может законно содержать художественные и аудиоресурсы, но эти файлы обычно требуют Git LFS или отдельной системы ресурсов.
Практическое правило таково: Git отлично подходит для исходного текста и небольших вспомогательных файлов. Git плох для часто изменяющихся бинарных блобов. Если файл не может быть осмысленно проверен в diff и часто изменяется, он, вероятно, не должен жить как обычный объект Git.
Распространенные кандидаты для Git LFS включают:
*.psd
*.ai
*.mp4
*.mov
*.wav
*.zip
*.uasset
*.fbx
*.blend
Будьте осторожны с широкими шаблонами изображений. Отслеживание каждого *.png в LFS может быть полезным для репозитория с большим количеством дизайна, но может раздражать для веб-приложения с множеством маленьких иконок. Шаблоны должны соответствовать файлам, которые действительно вызывают проблемы.
Используйте Git LFS для будущих больших файлов
Git LFS хранит небольшой файл-указатель в Git и сохраняет большой контент в хранилище LFS. Обычная история Git остается легче, в то время как пользователи все равно получают реальный файл в рабочем дереве, когда LFS загружает его.
Установите и инициализируйте его:
git lfs install
Отслеживайте шаблоны файлов, которые вам действительно нужны:
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes
git commit -m "Отслеживать большие дизайн- и видеофайлы с помощью Git LFS"
Файл .gitattributes важен. Зафиксируйте его, чтобы все использовали одни и те же правила LFS.
После этого добавляйте файлы как обычно:
git add demo.mp4
git commit -m "Добавить демо-видео продукта"
git push origin main
Коллега должен установить Git LFS перед работой с репозиторием. Если они клонируют без поддержки LFS, они могут увидеть файлы-указатели вместо реальных ресурсов, пока не установят LFS и не запустят:
git lfs pull
Также проверьте политику хранения и пропускной способности вашего Git-хоста. Git LFS решает проблему раздувания объектов Git, но не делает большие ресурсы бесплатными для хранения или передачи.
Миграция существующей истории
Включение LFS сегодня не исправляет автоматически вчерашние коммиты. Если 700 МБ архив был зафиксирован, а затем удален, он все еще может жить в истории. Очистка этого требует переписывания истории.
Переписывание истории изменяет идентификаторы коммитов. Любой, у кого есть существующий клон, должен тщательно пересинхронизироваться, и открытые pull request могут потребовать rebase или пересоздания. Делайте это в окне обслуживания и сначала сделайте резервную копию зеркала:
git clone --mirror [email protected]:ORG/REPO.git repo-backup.git
Затем работайте в свежем клоне. Убедитесь, что рабочее дерево чисто:
git status
Проверьте, что будет мигрировано:
git lfs migrate info --everything --above=10MB
Мигрируйте по шаблону, когда это возможно:
git lfs migrate import --everything --include="*.psd,*.mp4,*.zip"
Или мигрируйте файлы выше порога, если в репозитории много неизвестных больших файлов:
git lfs migrate import --everything --above=10MB
Проверьте результат перед отправкой:
git log --oneline --decorate -5
git lfs ls-files
git status
git lfs migrate info --everything --above=10MB
Если миграция сделала то, что вы ожидали, отправьте переписанные ветки и теги осознанно:
git push --force-with-lease origin main
git push --force-with-lease origin --tags
Для репозитория с множеством активных веток решите, какие ветки важны. Возможно, вам не нужно переписывать каждую заброшенную ветку, но любая ветка, которая все еще содержит большие объекты, может поддерживать удаленный репозиторий тяжелым.
После переписывания истории
Сообщите коллегам, что именно изменилось. Самая чистая инструкция — часто переклонировать. Если у людей есть локальная работа, они должны сначала сохранить ее:
git status
git branch my-work-before-lfs-migration
git fetch origin
git rebase origin/main
Для грязных локальных клонов переклонирование менее рискованно, чем попытка хирургически исправить старую историю.
Удаленное хранилище может не уменьшиться мгновенно. Провайдеры хостинга хранят недостижимые объекты некоторое время, и некоторые требуют поддержки или обслуживания репозитория, прежде чем обновятся цифры хранилища. Локально вы можете очистить старые объекты после того, как убедитесь, что миграция прошла успешно:
git reflog expire --expire=now --all
git gc --prune=now --aggressive
Не запускайте команды очистки как замену проверке. Они делают старые локальные объекты труднее восстановить.
Предотвращение той же проблемы в будущем
Добавьте проверку pre-commit или pre-receive, если большие случайные файлы продолжают появляться. Локальный хук pre-commit может предупредить разработчиков перед тем, как они зафиксируют большой артефакт. Серверное правило сильнее, потому что оно защищает общий репозиторий, даже если кто-то пропускает локальные хуки.
Простая локальная проверка может отклонять файлы больше выбранного размера, если они уже не отслеживаются LFS. Точный порог зависит от проекта. Сайт документации и игровой проект не должны использовать один и тот же лимит.
Также исправьте источник файлов. Если CI создает dist/, target/, отчеты о покрытии, архивы или скриншоты внутри репозитория, добавьте правильные записи в .gitignore:
dist/
target/
coverage/
*.log
*.zip
Не игнорируйте файлы вслепую. Убедитесь, что игнорируемые пути являются сгенерированными выходами, а не исходными входами.
Когда LFS — не ответ
Git LFS — это не универсальное хранилище артефактов. Артефакты сборки обычно принадлежат реестру пакетов, объектному хранилищу, ресурсам релиза или хранилищу артефактов CI. Дампы баз данных принадлежат хранилищу резервных копий. Большие наборы данных могут потребовать инструмента управления версиями данных или отдельного рабочего процесса хранения.
Цель — не спрятать каждый большой файл от Git. Цель — сохранить репозиторий достаточно быстрым, чтобы люди могли клонировать, создавать ветки, получать изменения и проверять без борьбы с инструментом.
Хорошая очистка оставляет три вещи: четкие правила .gitattributes для файлов, которые принадлежат LFS, правила .gitignore для файлов, которые никогда не должны быть зафиксированы, и короткую заметку для команды, объясняющую, как существующие клоны должны пересинхронизироваться. Это то, что не дает исправлению стать одноразовой очисткой, которую вы повторяете в следующем квартале.