Обслуживание долгоживущих Git-репозиториев на облачном Mac

Обслуживание долгоживущих Git-репозиториев на облачном Mac

После нескольких недель непрерывных сборок на облачном Mac команда git fetch по-прежнему может выполняться быстро, тогда как переключение веток, поиск базы слияния и обход объектов постепенно замедляются. Обычно проблема не в сети, а в том, что долгоживущий репозиторий накапливает множество незапакованных объектов, разрозненных pack-файлов, устаревших удалённых ссылок и блокировок, оставшихся после прерванных операций обслуживания. Удалить .git и заново клонировать репозиторий проще всего, но при этом будет потерян уже созданный кэш объектов, а сама проблема лишь отложится до следующего цикла.

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

Сначала зафиксируйте сопоставимое исходное состояние репозитория

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

git count-objects -vH
git rev-parse --git-dir
git rev-parse --git-common-dir
git for-each-ref --format='%(refname)' refs/remotes | wc -l
find "$(git rev-parse --git-common-dir)/objects/pack" -name '*.pack' | wc -l

Высокое значение count указывает на большое количество незапакованных объектов, а постоянный рост packs означает, что предыдущие операции создают всё новые небольшие pack-файлы. При использовании worktree обслуживание должно быть направлено на общий каталог, возвращаемый командой git rev-parse --git-common-dir; проверять только файл-указатель .git текущего worktree недостаточно.

Рекомендуется записывать значения count, in-pack, packs и size-pack до и после каждого обслуживания в обычный текстовый журнал. Не оценивайте успех только по сокращению занятого места: обновление графа коммитов может почти не влиять на объём данных, но заметно ускорять обход истории и поиск базы слияния.

Выбирайте инкрементные задачи вместо агрессивной очистки

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

git config maintenance.strategy incremental
git maintenance run \
  --task=commit-graph \
  --task=loose-objects \
  --task=incremental-repack

Граф коммитов ускоряет запросы достижимости в истории; индекс нескольких pack-файлов избавляет Git от необходимости сканировать каждый pack по отдельности; инкрементная перепаковка постепенно объединяет объекты, не создавая пиковых затрат времени и временного дискового пространства, характерных для полной сборки мусора.

Не используйте git gc --prune=now как регулярную задачу по расписанию. Немедленное удаление лишает вас периода, в течение которого данные можно восстановить, и может конфликтовать с параллельными процессами, всё ещё использующими ссылки на старые объекты.

Даже если в репозитории только что массово удалили ветки, сначала следует сохранить стандартный льготный период. Более глубокую очистку стоит рассматривать лишь после того, как вы убедились, что не выполняются сборки, переключение рабочих копий, перебазирование, обновление подмодулей или архивирование.

Управляйте удалёнными ссылками отдельно

Удалённые отслеживаемые ветки, которые долго не очищались, увеличивают область обхода. Сначала просмотрите предполагаемые изменения, а затем выполните очистку:

git remote prune origin --dry-run
git remote prune origin

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

Изолируйте сборку и обслуживание атомарной блокировкой

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

common_dir="$(git rev-parse --git-common-dir)"
lock_dir="$common_dir/maintenance.lock"

if ! mkdir "$lock_dir" 2>/dev/null; then
  printf '%s\n' "maintenance skipped: repository is busy"
  exit 0
fi

trap 'rmdir "$lock_dir"' EXIT INT TERM

git maintenance run \
  --task=commit-graph \
  --task=loose-objects \
  --task=incremental-repack

Точка входа сборки также должна проверять или удерживать ту же блокировку, иначе односторонняя блокировка в сценарии обслуживания бесполезна. Несколько worktree не должны создавать отдельные блокировки в своих рабочих каталогах, поскольку все они используют одно хранилище объектов.

В каталог блокировки можно записывать идентификатор процесса и время запуска для ручной диагностики, но не удаляйте его только потому, что указанное время выглядит устаревшим. Сначала с помощью ps убедитесь, существует ли процесс, а затем проверьте, не выполняются ли git pack-objects, git index-pack или git maintenance.

Проверяйте индексы и связность объектов

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

git commit-graph verify
git multi-pack-index verify
git fsck --connectivity-only

Эти проверки можно включить в конвейер по следующей схеме:

| Проверка | Назначение | Действие при ошибке | |---|---|---| | `commit-graph verify` | Проверить уровни и ссылки графа коммитов | Перезаписать граф коммитов и проверить снова | | `multi-pack-index verify` | Проверить указатели индекса нескольких pack-файлов | Перестроить индекс, не удаляя pack-файлы вручную | | `fsck --connectivity-only` | Проверить наличие всех достижимых объектов | Прекратить повторное использование репозитория и получить недостающие данные из доверенного удалённого источника | | `count-objects -vH` | Сравнить структуру до и после обслуживания | Сохранять динамику, не задавая произвольных порогов |

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

Встройте обслуживание в жизненный цикл узла

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

На облачном Mac от VMOak сценарий обслуживания рекомендуется хранить за пределами репозитория, в доступном только для чтения каталоге эксплуатации, а целевой путь передавать из планировщика. Сценарий не должен читать одноимённые файлы из веток проекта, чтобы обычные изменения кода не могли переписать логику обслуживания. Если на узле размещено несколько репозиториев, каждый из них следует блокировать и проверять отдельно; сбой одного репозитория не должен запускать очистку остальных.

Итоговая приёмка не должна ограничиваться подтверждением того, что «места стало больше». Нужно убедиться, что повседневные операции не замедлились: выполните обновление из удалённого репозитория, переключение на целевой коммит, поиск базы слияния и подготовку сборки в режиме только для чтения, а затем сравните журналы до и после обслуживания. Только так обслуживание Git становится аудируемой операцией на узле, а не экстренной командой удаления, запускаемой при нехватке дискового пространства.

Часто задаваемые вопросы

Можно ли обслуживать Git во время активной сборки?

Не запускайте очистку одновременно с checkout, rebase или упаковкой. Используйте атомарную блокировку репозитория и выполняйте задачу в период простоя.

Почему не стоит регулярно выполнять git gc --prune=now?

Немедленное удаление лишает команду окна восстановления и повышает риск удаления объектов, которые ещё нужны параллельному процессу. Безопаснее инкрементальная перепаковка.

Что проверять после обслуживания?

Проверьте commit-graph, multi-pack-index и связность объектов, а также сохраните число pack-файлов, свободных объектов и объём занятого места.

Выделенный физический узел

Выберите облачный Mac для сборки, автоматизации или удалённой работы с контентом

Сравните конфигурации M4 16GB и M4 Pro 64GB, проверьте срок аренды и доступные узлы, затем оформите заказ.

Выбрать конфигурацию облачного Mac