파이프라인이 checkout 단계에서 갑자기 bad object, missing blob, pack checksum mismatch를 보고할 때 다시 실행하면 정상으로 돌아올 수도 있지만, 오히려 더 많은 작업이 동시에 실패할 수도 있습니다. 이런 현상은 대개 비즈니스 코드의 오류가 아니라 작업 공간이나 객체 캐시, Git 팩 파일의 불일치 때문에 발생합니다. 올바른 대응 순서는 캐시부터 즉시 삭제하는 것이 아닙니다. 먼저 쓰기 작업을 중단하고 현장을 보존한 다음, 손상이 단일 저장소에 국한되는지 공유 계층까지 영향을 받는지 판단해야 합니다.
먼저 네트워크 장애와 객체 손상 구분하기
네트워크 장애는 주로 가져오기 단계에서 발생하며, 로그에는 일반적으로 연결 중단, 원격의 예기치 않은 연결 종료 또는 완료되지 않은 전송이 기록됩니다. 반면 객체 손상은 체크아웃, 병합, 기록 조회 또는 아카이브 생성 중에 자주 나타나며 구체적인 객체 해시를 동반합니다. 실패한 명령, 종료 코드, 커밋 ID, 작업 공간 경로와 캐시 세대를 먼저 기록해야 합니다. 파이프라인 로그의 마지막 몇십 줄만 보관해서는 안 됩니다.
우선 다음과 같은 읽기 전용 검사를 실행할 수 있습니다.
git status --porcelain=v2
git rev-parse --show-toplevel
git rev-parse HEAD
git fsck --full --strict --no-dangling
git fsck가 missing blob을 보고하면 특정 참조에서 도달 가능한 파일 객체가 없다는 뜻입니다. 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가 작업 공간을 언제든 다시 만들 수 있다면 원래 위치에서 복구를 시도하는 대신 전체 디렉터리의 이름을 바꿔 격리하는 것이 가장 안전합니다. 격리한 디렉터리는 읽기 전용으로 설정하고, 연결된 작업과 캐시 출처를 기록해야 합니다.
느슨한 객체, 팩 파일과 공유 캐시에서 손상 위치 찾기
Git 객체는 느슨한 파일 형태로 존재할 수도 있고 .pack 파일에 포함될 수도 있습니다. 오류에 객체 해시가 표시되면 먼저 객체 유형을 조회합니다.
object="0123456789abcdef0123456789abcdef01234567"
git cat-file -t "$object"
git cat-file -s "$object"
명령이 실패하면 .git/objects/${object:0:2}/${object:2}가 존재하는지 확인합니다. 파일은 있지만 읽을 수 없다면 일반적으로 내용이 잘렸거나 검증에 실패했거나 하위 저장 계층에서 복사가 불완전하게 끝난 것입니다. 출처를 확인하지 않은 다른 위치에서 같은 이름의 파일을 가져와 현장에 복사해서는 안 됩니다.
팩 파일은 각 인덱스를 개별적으로 검증합니다.
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의 구성, 리전, 네 가지 결제 주기를 비교한 후 주문을 생성하세요.