빌드노트
원격에서 지운 브랜치가 fetch --prune 뒤에도 남을 때
fetch --prune 뒤 남은 topic이 로컬 브랜치인지 원격 추적 참조인지 확인한다. 표준 refspec에서의 정리 범위와 로컬 커밋·HEAD 보존을 비교하고, 로컬 삭제 전 남은 작업을 확인하는 명령을 설명한다.
원격에서 topic 브랜치를 지우고 git fetch --prune origin까지 실행했는데, git branch -a에 topic이 남아 있을 수 있다. 일반적인 clone 설정에서 prune은 사라진 원격 브랜치의 추적 참조를 정리한다. 내가 작업하는 로컬 브랜치를 함께 지우지는 않는다. 목록에 남은 이름이 어느 쪽인지 먼저 확인한다.
topic과 origin/topic을 구분한다
Git이 설치된 터미널에서 확인할 저장소를 연다. 여기서는 원격이 origin, 삭제된 원격 브랜치와 대응하는 로컬 브랜치가 모두 topic이다. 자신의 이름으로 바꿔 아래 조회를 실행한다. 첫 명령은 목록을, 두 번째는 fetch 매핑을, 마지막은 전체 참조 이름과 커밋을 보여 준다.
git branch -a
git config --get-all remote.origin.fetch
git for-each-ref --format="%(refname) %(objectname:short=7)" refs/heads refs/remotes/origin
branch -a의 topic은 로컬 브랜치이고, remotes/origin/topic은 원격 브랜치의 위치를 내 저장소에 기록한 원격 추적 브랜치다. 둘 다 내 저장소에 있으므로 뒤의 이름이 보인다고 지금 서버에도 브랜치가 있다고 단정할 수는 없다. 별표는 현재 체크아웃한 브랜치를 표시한다. remotes/origin/HEAD -> origin/main은 기본 브랜치를 가리키는 별도 표시다. git-branch의 목록 설명 (새 탭에서 열림)
일반적인 fetch 매핑은 +refs/heads/*:refs/remotes/origin/*다. 콜론 왼쪽은 원격에서 읽을 참조이고, 오른쪽은 내 저장소에 기록할 위치다. 이 매핑에서는 원격의 refs/heads/topic이 내 refs/remotes/origin/topic에 대응한다. 내 로컬 브랜치인 refs/heads/topic은 이 매핑의 갱신 대상에 들어 있지 않다. git-fetch의 매핑 설명 (새 탭에서 열림)
prune 전후에 사라진 참조를 확인한다
Windows 11, Git 2.45.1.windows.1에서 새 로컬 bare 원격과 두 clone으로 비교했다. 한쪽에서 합성 원격의 topic을 삭제했고, 받는 쪽 topic에는 아직 보내지 않은 커밋을 하나 더 두었다. 실제 서비스의 원격은 사용하지 않았다.
아래는 원격 삭제 뒤 일반 fetch와 prune을 차례로 실행한 결과를 모은 비교다. 커밋 해시는 실험에서 나온 값이며, 자기 저장소에서는 참조의 유무와 커밋이 유지되는지를 읽는다.
참조 일반 fetch 뒤 --prune 뒤
refs/heads/topic f631295 f631295
refs/remotes/origin/topic c94de88 없음
HEAD -> refs/heads/topic f631295 f631295
원격 삭제 직후에도 받는 clone의 두 참조는 남아 있었다. git fetch origin은 성공했지만 오래된 추적 참조를 지우지 않았다. 이 비교에서는 자동 prune을 껐다. fetch.prune이나 remote.origin.prune이 켜진 저장소에서는 일반 fetch도 정리할 수 있다.
실행 전 git fetch --dry-run --prune origin으로 정리 대상을 미리 볼 수 있다. 실험에서는 - [deleted] (none) -> origin/topic이 표시됐지만 참조·HEAD·파일은 그대로였다. 대상이 예상과 맞으면 git fetch --prune origin을 실행한 뒤 첫 조회를 반복한다. 성공한 prune 뒤에는 추적 참조만 사라졌고, 로컬 커밋과 체크아웃된 HEAD, 파일의 SHA-256은 모두 유지됐다. 삭제 표시는 내 추적 참조를 정리했다는 뜻이다. 서버에 삭제 요청을 보낸 결과가 아니다.
fetch가 오류로 끝났다면 정리가 완료됐다고 읽지 않는다. 성공 뒤에도 refs/remotes/origin/topic이 남는다면 원격 삭제 여부, fetch한 원격 이름, 앞에서 조회한 매핑을 확인한다. 반면 refs/heads/topic만 남았다면 이 실험처럼 로컬 브랜치가 보존된 상태다.
git-fetch의 pruning 설명 (새 탭에서 열림)은 정리 범위가 refspec에 달려 있다고 명시한다. 매핑이 다르거나 추가 행이 나오면 그 대상을 먼저 읽는다. 태그 매핑이나 --prune-tags를 더하면 로컬 태그도 정리 대상이 될 수 있으므로 이 브랜치 문제를 해결하려고 임의로 추가하지 않는다.
prune 뒤 git branch -vv에는 [origin/topic: gone]이 표시됐다. gone은 설정된 upstream 참조를 이제 찾을 수 없다는 뜻이다. 로컬 커밋이 사라졌거나 로컬 브랜치를 지워도 된다는 판정은 아니다. 참조 조회의 upstream 설명 (새 탭에서 열림)
로컬 브랜치는 남은 작업을 보고 정리한다
원격 브랜치 삭제와 작업 완료는 같은 판정이 아니다. 로컬 topic을 지우기 전에 미커밋 파일과 통합 대상에 없는 커밋을 확인한다. 다음은 최신 fetch가 성공한 origin/main을 통합 대상으로 삼은 조회다. 팀의 통합 대상이 다르면 그 참조로 바꾼다. 조회가 실패하면 빈 결과로 해석하지 말고 오류부터 해결한다.
git status --porcelain=v1 --untracked-files=all
git log --oneline origin/main..topic
실험에서 status는 성공했고 출력이 비어 있었다. 추적 파일의 미커밋 변경과 추적하지 않는 파일이 표시되지 않은 상태이며, ignored 파일까지 없다는 뜻은 아니다. 파일이 표시되면 내용을 확인하고 필요한 변경을 커밋하거나 별도로 보관한 뒤 브랜치를 옮긴다.
log에는 local-only note와 published topic 두 커밋이 남았다. origin/main..topic은 topic에서 도달할 수 있지만 origin/main에서는 도달할 수 없는 커밋 범위다. 이 실험에서는 두 커밋을 main에 통합하지 않아 로컬 브랜치를 남겼다. squash나 rebase로 반영한 프로젝트라면 같은 내용이 다른 커밋으로 들어갔을 수도 있으므로 통합 기록도 확인한다. git-log의 범위 설명 (새 탭에서 열림)
보존할 작업이 없고 정리하기로 결정했을 때는 대상 브랜치에서 벗어난 뒤 git branch -d topic을 쓴다. 실험에서는 깨끗한 상태에서 git switch main으로 옮긴 다음 삭제를 시도했지만, not fully merged 오류와 종료 코드 1로 거절됐다. 거절 뒤에도 로컬 참조와 커밋은 유지됐다. 반대로 main과 같은 커밋에 만든 별도 브랜치 finished는 git branch -d finished로 삭제됐다.
git-branch의 삭제 옵션 (새 탭에서 열림)에서 -d의 병합 확인 기준은 upstream이며, upstream 설정이 없으면 HEAD다. 따라서 삭제 성공만으로 자신이 정한 통합 대상에 반영됐다고 가정하지 말고 앞의 로그도 함께 읽는다. 거절 안내의 -D로 바로 바꾸면 병합 확인을 건너뛴다. 남길 커밋을 확인하지 못했다면 로컬 브랜치를 유지한다.
직접 확인한 범위는 표준 fetch 매핑의 로컬 bare 원격과 두 clone에서의 삭제·fetch·prune, 미전송 커밋과 HEAD 보존, 로컬 삭제의 거절과 성공이다. mirror·특수 refspec·태그 정리·shallow clone·여러 원격과 worktree·실제 서버의 인증과 네트워크는 시험하지 않았다. 미커밋 변경 보관이나 squash·rebase 뒤의 삭제 판단도 재현하지 않았다.