Диагностика и безопасное восстановление объектов Git в Cloud Mac CI

Диагностика и безопасное восстановление объектов Git в Cloud Mac CI

Если после checkout конвейер внезапно завершается с ошибкой bad object, missing blob или pack checksum mismatch, повторный запуск иногда проходит успешно, а иногда приводит к одновременному сбою ещё большего числа задач. Обычно причина не в прикладном коде, а в рассогласовании рабочей копии, кэша объектов или pack-файлов Git. Правильный порядок действий — не очищать кэш немедленно, а сначала остановить запись, сохранить состояние системы и определить, затронуто ли только одно хранилище или общий слой.

Сначала отличите сетевой сбой от повреждения объектов

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

Для начала можно выполнить проверки, не изменяющие хранилище:

git status --porcelain=v2
git rev-parse --show-toplevel
git rev-parse HEAD
git fsck --full --strict --no-dangling

Сообщение missing blob от git fsck означает, что отсутствует объект файла, достижимый из одной из ссылок. Ошибка missing tree обычно препятствует извлечению структуры каталогов, а invalid sha1 pointer указывает, что ссылка ведёт на объект, который невозможно прочитать. Наличие только dangling-объектов само по себе не означает повреждения хранилища: они могли остаться после перебазирования, упаковки или замены коммитов.

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

Заморозьте рабочую копию и сохраните доказательства

Сначала приостановите runner, периодические операции получения данных и задачи обновления кэша, которые могут записывать в тот же каталог. Не запускайте в исходном каталоге git gc, git prune, переупаковку или рекурсивное удаление. Эти операции могут изменить расположение объектов и помешать воспроизвести первоначальную ошибку.

Как минимум сохраните полный журнал задачи, .git/HEAD, .git/config, .git/packed-refs, .git/refs, .git/logs, текущее состояние и незакоммиченные изменения. В рабочем каталоге исходного кода могут находиться неотслеживаемые файлы. Перед архивацией просмотрите их список, чтобы случайно не включить учётные данные или крупные артефакты сборки.

incident="$HOME/git-incidents/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$incident"
git status --porcelain=v2 > "$incident/status.txt"
git show-ref --head > "$incident/refs.txt"
git reflog show --all --date=iso > "$incident/reflog.txt"
git diff --binary > "$incident/worktree.patch"
git diff --cached --binary > "$incident/index.patch"
git fsck --full --strict > "$incident/fsck.txt" 2>&1 || true

Если CI может в любой момент пересоздать рабочую копию, надёжнее всего переименовать весь каталог и изолировать его, а не пытаться исправлять на месте. Изолированный каталог следует перевести в режим только для чтения и записать, какой задаче и какому источнику кэша он соответствует.

Найдите повреждение среди loose-объектов, pack-файлов и общего кэша

Объекты Git могут храниться как отдельные loose-файлы или входить в состав .pack. Если в сообщении об ошибке указан хеш, сначала можно запросить тип объекта:

object="0123456789abcdef0123456789abcdef01234567"
git cat-file -t "$object"
git cat-file -s "$object"

Если команда завершается ошибкой, проверьте наличие .git/objects/${object:0:2}/${object:2}. Если файл существует, но не читается, это обычно указывает на усечённое содержимое, несовпадение контрольной суммы или незавершённое копирование на нижележащем уровне. Не заменяйте исходный файл одноимённым файлом из другого источника, надёжность которого не подтверждена.

Для pack-файлов проверьте каждый индекс:

for index in .git/objects/pack/*.idx; do
  git verify-pack -v "$index" >/dev/null || printf '%s\n' "$index"
done

Если в хранилище настроены alternates, также проверьте .git/objects/info/alternates. Когда несколько задач совместно используют один доступный для записи каталог объектов, одна прерванная операция записи может повредить все рабочие копии, которые на него ссылаются. В таком случае повторного клонирования одного хранилища недостаточно: необходимо прекратить публикацию этого поколения кэша.

Проверьте, воспроизводится ли повреждение

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

Восстанавливайте из чистого клона, а не исправляйте на месте

Для CI-рабочей копии без ручных изменений безопасное восстановление обычно состоит в создании нового каталога, полном получении целевого коммита, проверке и атомарной замене старого каталога. Новый клон не должен использовать кэш объектов, целостность которого ещё не подтверждена.

root="$HOME/ci-workspaces"
next="$root/project.next"
active="$root/project"
failed="$root/project.failed"

rm -rf "$next"
git clone --no-local "$REPOSITORY_PATH" "$next"
git -C "$next" checkout --detach "$EXPECTED_COMMIT"
git -C "$next" fsck --full --strict
test "$(git -C "$next" rev-parse HEAD)" = "$EXPECTED_COMMIT"
mv "$active" "$failed"
mv "$next" "$active"

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

Включите проверку объектов в процесс публикации кэша

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

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

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

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

Можно ли сразу удалить подозрительный файл из .git/objects?

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

Исправляет ли git gc повреждённую базу объектов?

git gc не является средством восстановления. Команда переписывает и удаляет объекты, поэтому может скрыть причину сбоя. Одноразовую рабочую копию CI безопаснее пересоздать и проверить.

Как не допустить распространения повреждённого кеша Git?

Публикуйте кеш неизменяемыми поколениями. Новое поколение создавайте отдельно, проверяйте через git fsck и активируйте только атомарным переименованием каталога.

Эксклюзивный физический узел

Выберите облачный Mac для непрерывной сборки

Сравните конфигурации, регионы и четыре варианта расчётного периода SDKMac M4 и SDKMac M4 Pro, затем оформите заказ.

Выбрать тариф