기본 브랜치를 바꿨는데 새 worktree 가 옛 브랜치에서 분기될 때

로컬 clone 의 refs/remotes/origin/HEAD 는 clone 시점에 한 번만 설정되고 이후 자동 갱신되지 않는다. origin/HEAD 를 보고 분기하는 도구는 전부 옛 기본 브랜치를 쓴다.

저장소 기본 브랜치를 main 에서 develop 으로 바꾼 지 한참 됐다. 확인도 했다.

$ gh repo view --json defaultBranchRef -q .defaultBranchRef.name
develop

그런데 새 worktree 를 만들었더니 origin/main 의 옛날 커밋에서 분기돼 있었다. git log --oneline -5 를 찍으니 이미 develop 에 머지된 최신 커밋들이 하나도 안 보였다. 몇 주 전 코드 위에서 작업을 시작할 뻔했다.

원인 — 로컬 캐시가 따라오지 않는다

“기본 브랜치에서 분기”라는 동작은 대개 GitHub API 를 매번 조회하지 않는다. 로컬 clone 에 캐시된 심볼릭 참조를 본다.

두 값을 나란히 찍어보면 어긋나 있다.

$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/main          # ← stale

$ git remote show origin | grep 'HEAD branch'
  HEAD branch: develop            # ← GitHub 의 실제 값

refs/remotes/origin/HEADgit clone 시점에 한 번 설정되고 그 뒤로는 자동 갱신되지 않는다. gh repo edit --default-branch 로 바꾸든 웹 UI 에서 바꾸든 마찬가지다. git fetch 를 아무리 돌려도 이 참조는 그대로다.

여기서 헷갈리기 쉬운 게, 설정을 고치는 커밋을 이미 머지했더라도 소용없다는 점이다. 나도 “worktree base 브랜치를 origin/mainorigin/develop 으로 통일”이라는 PR 을 이미 머지한 상태였다. 그건 저장소 안의 설정 이야기고, 이건 각 로컬 clone 이 개별적으로 들고 있는 캐시 이야기다. 기본 브랜치 변경 이전부터 갖고 있던 clone 은 전부 각자 재동기화해야 한다.

해결

한 줄이다.

$ git remote set-head origin -a
'origin/HEAD' has changed from 'main' and now points to 'develop'

-a 는 원격에 물어봐서 자동으로 맞추라는 뜻이다. 확인한다.

$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/develop

언제 의심할 것인가

증상이 “에러”가 아니라 “조용히 옛 코드”라서 늦게 발견된다. 두 가지 신호를 기억해두면 된다.

기본 브랜치를 바꾼 저장소인데, 그 이전부터 로컬에 clone 이 있었다면 무조건 한 번 확인한다. 새로 clone 한 기기에서는 정상이고 오래된 기기에서만 틀리는 형태라, 기기마다 증상이 갈리는 것도 힌트다.

새 worktree 를 만들었는데 git log --oneline -5 가 예상보다 훨씬 옛 커밋만 보이면 즉시 두 값을 대조한다.

git symbolic-ref refs/remotes/origin/HEAD
git remote show origin | grep 'HEAD branch'

이미 잘못 분기한 worktree

아직 커밋을 쌓지 않았다면 그냥 최신 기준으로 옮기면 된다.

git rebase origin/develop

새 커밋이 없으면 사실상 fast-forward 라 위험하지 않다. 커밋이 쌓인 뒤에 발견했다면 그때도 rebase 로 되지만 충돌을 볼 각오는 해야 한다. 그래서 worktree 를 만든 직후 로그를 한 번 보는 습관이 싸게 먹힌다.

정리

git 은 원격의 기본 브랜치를 “clone 할 때 참고한 값”으로 취급하지, 계속 추적하는 값으로 보지 않는다. 그래서 기본 브랜치를 바꾸는 작업에는 모든 로컬 clone 에서 git remote set-head origin -a 라는 후속 단계가 붙는다.

기본 브랜치를 바꾸면 이슈 자동 close 동작도 같이 바뀐다. PR body 의 Closes #N 이 안 먹는 문제는 닫히지 않는 Closes #N에 따로 적었다. 둘은 별개 증상인데 원인은 같은 전환 작업에서 나온다.