dubious ownership은 명령을 실행한 사용자와 폴더 주인이 달라 깃이 저장소 설정을 읽지 않고 멈춘 상태입니다. 소유자를 맞추거나 전역 safe.directory에 그 경로만 넣습니다. 저장소 설정에 넣은 예외는 읽히지 않습니다. 푸시 거절, 소켓 권한과 줄을 가릅니다. Git, 도커, 한국 맥 개발. 2026년 9월 깃 문서.
dubious ownership은 커밋이 깨져서가 아니라, 명령을 실행한 사용자와 폴더 주인이 달라 깃이 그 설정을 읽지 않겠다고 멈춘 상태입니다. 푸시 전에 폴더 주인부터 보세요.
도커 볼륨을 컨테이너의 다른 계정이 열면 detected dubious ownership 이 납니다. 원격이 앞으로 감기를 거절한 것과는 다릅니다. 이쪽은 상태 조회조차 거부합니다.
내 파일이면 소유자를 맞추고, 나눠 쓰는 경로만 전역 설정에 예외로 넣습니다. 저장소 설정에 넣은 값은 깃이 믿지 않습니다. 근거는 git config와 깃허브 보안 안내입니다.
이 한 줄이 거절하는 것
다른 사람의 저장소 설정을 실행하지 않으려고 멈춘 것입니다. 깃 2.35.2 이후에는 작업 폴더의 소유자가 현재 사용자가 아니면, 그 저장소의 설정과 훅을 읽기 전에 죽습니다. 공유 폴더에 심은 설정이 내 계정으로 명령을 실행하는 일을 막기 위한 검사입니다.
그래서 git status, git pull, 서브모듈 갱신이 같은 문장으로 끝날 수 있습니다. 원격 브랜치와의 관계는 아직 묻지 않은 상태입니다. 메시지에 적힌 경로가 검사에 걸린 폴더이고, 바로 아래에 전역 설정으로 예외를 넣는 예시 명령이 따라옵니다.
예외를 아무 데나 넣으면 검사가 무력해집니다. 문서의 safe.directory 는 보호된 설정에서만 읽습니다. 시스템 설정, 전역 설정, 명령줄 -c 는 보고, 그 저장소의 .git/config 는 보지 않습니다. 공격자가 저장소 안에 예외를 써 넣는 길을 막은 규칙입니다.
먼저 확인할 것 | 메시지 경로에서 ls -ld 로 소유자와 id 로 내 사용자를 나란히 보세요. 숫자가 다르면 푸시 옵션을 바꾸기 전에 그 차이부터 정리합니다.
폴더 주인이 명령 사용자와 다르면 깃은 그 저장소 설정을 읽지 않는다
도커와 CI에서 주인이 갈릴 때
노트북에서 클론한 폴더는 보통 내 사용자 소유라 이 문장이 안 납니다. 이미지 안에서 소스를 볼륨으로 붙이고, 프로세스 사용자가 node(uid 1000)인데 파일 소유자는 root이면 컨테이너 안의 git만 거절합니다. 호스트의 터미널은 그대로 동작해서 더 헷갈립니다.
액션도 비슷합니다. 체크아웃은 한 사용자가 받고, 뒤 스텝이 다른 사용자로 같은 경로를 만지면 두 번째 스텝만 dubious ownership 으로 죽습니다. 서브모듈 경로는 따로 검사하므로, 상위 저장소는 넘어가도 하위 경로에서 같은 문장이 납니다.
윈도우와 WSL을 오가면 소유자 숫자가 달라 보이기도 합니다. 깃 배시의 깃과 WSL 안의 깃은 서로의 전역 설정을 공유하지 않을 수 있어, 한쪽에만 safe.directory를 넣으면 다른 쪽 터미널은 계속 멈춥니다. 멈추는 그 실행 파일의 전역 설정에 넣어야 합니다.
상황
소유자가 갈리는 이유
먼저 할 일
내 노트북 클론
보통 소유자가 같음
이 오류가 아니면 다른 글
도커 볼륨
root 파일, 컨테이너 uid 1000
사용자 맞추기 또는 예외 한 경로
액션 다음 스텝
체크아웃 사용자와 다름
같은 사용자로 이어서 실행
서브모듈
하위 경로를 따로 검사
메시지에 찍힌 그 경로
소유자와 내 사용자를 비교
# 메시지에 찍힌 경로를 그대로 넣는다
ls -ld /work/app
id
# 소유자 숫자와 uid 가 다르면 아래 둘 중 하나로 간다
safe.directory를 넣는 위치
예외는 전역이나 시스템 설정에 둡니다. git config --global --add safe.directory /work/app 는 내 계정의 ~/.gitconfig 에 한 경로를 추가합니다. --add 를 빼면 예전에 넣어 둔 다른 경로를 덮어쓸 수 있으니, 경로를 늘릴 때는 --add 를 유지하세요.
별표 하나는 모든 저장소를 예외로 둡니다. 문서도 그 값이면 소유자 검사를 통째로 끈다고 적습니다. 혼자 쓰는 개인 노트북의 로컬 디스크라면 이해가 되지만, 공유 드라이브나 여러 사람이 쓰는 러너에는 넣지 않는 편이 맞습니다. 검사가 생긴 이유가 그 공유 경로이기 때문입니다.
빈 값을 하나 넣으면 목록이 초기화됩니다. 시스템 설정에 별표가 이미 있고 다시 조이고 싶을 때, 전역에서 빈 값을 먼저 추가한 다음 허용할 경로만 적습니다. 경로는 저장소 최상위입니다. 베어 저장소면 깃 디렉터리 쪽을 넣습니다. 메시지에 찍힌 경로를 그대로 쓰는 것이 가장 덜 헷갈립니다.
한 경로만 전역 예외로 추가
git config --global --add safe.directory /work/app
# 이 명령 한 번만 예외로 두고 설정 파일은 그대로
git -c safe.directory=/work/app status
# 저장소 .git/config 에 넣어도 깃은 읽지 않는다
예외는 전역 설정에 두고, 저장소 설정 파일에는 넣지 않는다
폴더 소유자를 맞추는 쪽
그 파일을 내 계정이 소유해야 맞다면 예외보다 소유자 변경이 낫습니다. sudo chown -R "$(id -u):$(id -g)" /work/app 는 그 트리의 주인을 지금 사용자로 바꿉니다. 호스트에서 편집기도 같이 쓰는 폴더라면, 컨테이너만 주인을 바꾸면 호스트 저장이 다시 root로 돌아갈 수 있습니다. 그때는 컨테이너 사용자를 호스트 uid와 같게 맞춥니다.
도커 소켓 권한이 없어 docker 명령 자체가 거절되는 문제와는 다릅니다. 그건 소켓 그룹이고, 이쪽은 소스 폴더의 소유자입니다. 소켓이 막히면 docker.sock 권한을 보고, 깃 문장만 나오면 폴더 주인을 보면 됩니다.
액션에서는 chown보다 사용자를 통일하는 편이 단순합니다. actions/checkout 이 받은 사용자로 다음 스텝도 돌리면 검사가 통과합니다. 이미지를 root로 돌려 소스에 쓰는 스텝이 있으면, 그 스텝이 남긴 파일 때문에 다음 잡의 깃이 다시 멈춥니다.
푸시 거절과 다른 점
non-fast-forward 는 원격이 커밋을 받은 뒤, 앞으로 감기가 아니라며 거절한 줄입니다. 로컬 status는 됩니다. dubious ownership 은 status부터 안 됩니다. 원격 브랜치를 병합하거나 리베이스해도 폴더 주인이 다르면 같은 문장이 남습니다. 푸시 거절은 non-fast-forward 글의 순서입니다.
훅이 커밋을 막는 경우도 문장이 다릅니다. 로컬 pre-commit 이 실패하면 훅 이름이 나오고, 원격이 거절하면 remote rejected 처럼 원격 이유가 붙습니다. 소유자 검사는 훅이 실행되기 전에 저장소 설정을 거절하므로, 훅 로그가 아예 없습니다.
포트가 이미 쓰여 서버가 못 앉는 줄과도 관계가 없습니다. 개발 서버의 EADDRINUSE는 포트 점유에서 프로세스를 찾으면 됩니다. 깃 문장에 repository at 경로가 있으면 그 경로의 소유자만 보면 됩니다.
문장
막힌 곳
손댈 것
dubious ownership
로컬 폴더 주인
chown 또는 전역 예외
non-fast-forward
원격 브랜치 이력
fetch 후 병합 또는 리베이스
remote rejected
원격 훅, 보호 설정
거절 이유 문구
docker.sock 권한
소켓 그룹
사용자 그룹, 소켓 모드
한국에서 맥과 도커를 같이 쓰는 경우에는 볼륨 주인이 root인지만 봐도 절반은 갈립니다. 호스트 터미널의 git status가 되면 저장소는 정상입니다. 컨테이너 안에서만 죽으면 그 프로세스의 uid를 보면 됩니다.