기술 · 실용 가이드
Git submodule에 “not our ref”가 뜨나요? 교체 전에 기록된 커밋부터 추적하세요
해시는 예상한 브랜치가 아니라 상위 저장소에서 옵니다. gitlink, 실제 URL, 로컬 객체와 원격 도달 가능성을 기록한 뒤 sync, push, fork 또는 의도적인 교체를 선택하세요.

“not our ref”는 --force를 더 붙이라는 뜻이 아닙니다. 상위 저장소가 요청한 정확한 객체를 현재 경로가 제공하지 못했다는 뜻입니다.
안전한 순서는 기록, 경로, 객체, 복구입니다. gitlink OID와 실제 URL을 확인하고 로컬 존재 및 원격 도달성을 증명한 뒤 최소한의 수리를 선택합니다.
실패 상태를 보존하고 gitlink 읽기
rm -rf, deinit, --force, --remote보다 먼저 실패한 상위 커밋, 전체 오류, submodule 경로와 로컬 작업을 보존하세요. git ls-tree HEAD -- path/to/submodule의 mode 160000 항목이 정확한 OID입니다.
이 OID가 첫 증거입니다. .gitmodules의 브랜치 이름은 핀을 대체하지 않습니다. CI가 다른 해시를 보이면 현재 브랜치가 아니라 실제 실패한 상위 커밋을 검사하세요.
Git이 실제로 쓰는 URL 확인
.git/config와 .gitmodules의 submodule URL을 비교하세요. 초기화 때 URL이 복사되며 이후 .gitmodules를 바꿔도 오래된 checkout은 자동 갱신되지 않습니다. 맞는 OID를 틀린 저장소에 요청할 수 있습니다.
의도한 이전이라면 git submodule sync --recursive 후 다시 확인하세요. sync는 객체나 권한을 만들지 않습니다. 자격 증명 없이 실제 URL을 기록하고 CI의 비밀 범위를 바로잡으세요.
로컬 존재, 광고된 ref, 가져오기 가능성 분리
submodule에서 git cat-file -e OID^{commit}으로 객체를 확인하고 원래 개발자에게만 있는 객체 저장소를 보존하세요. 흔한 원인은 submodule 커밋을 push하기 전에 상위 gitlink를 push한 것입니다.
git ls-remote --heads --tags URL은 광고된 ref만 보여 주며 모든 도달 불가 객체를 나열하지 않습니다. 일치 항목이 없어도 삭제 증거는 아닙니다. 같은 인증으로 일반 fetch를 시도하고 결과를 보관하세요.
증거에 맞는 복구 선택
URL이 오래됐으면 같은 OID를 유지한 채 sync하세요. 객체가 로컬에만 있으면 소유한 원격으로 push하거나 fork를 만들고 URL을 명시적으로 갱신하세요. 호스트에 있는데 CI만 못 가져오면 권한을 고치세요.
OID가 정말 복구 불가하거나 의도적으로 의존성을 바꿀 때만 검증한 대체 커밋을 시험하고 submodule 경로를 add해 새 gitlink를 commit하세요. 이전·새 OID, URL, 이유를 기록합니다.
--remote와 새 clone을 증거로 착각하지 않기
일반 update는 기록 OID를, --remote는 원격 브랜치 끝을 사용합니다. 성공해도 다른 커밋일 수 있으므로 브랜치 추적이 정책일 때만 쓰세요.
깨끗한 clone은 수리 뒤 재현성 시험입니다. push --recurse-submodules=check/on-demand, URL 변경 검토, 정확한 CI checkout으로 재발을 막으세요.
