브랜치 통합 함정 — 로컬 ref ≠ 원격 ref, orphan main과 non-ff
로컬 ref만 보고 ff로 판단했다가 원격에서 non-fast-forward로 거부되는 orphan 브랜치 함정과 해법.
브랜치 통합 함정 — 로컬 ref ≠ 원격 ref, orphan main과 non-ff
main·master가 뒤섞인 repo를 하나로 정리할 때, 로컬 ref만 보고 “선형이니 ff로 끝”이라 판단하면 원격에서 non-fast-forward로 거부될 수 있다. 로컬 main과 origin/main이 같은 이름·같은 커밋 메시지여도 실체(히스토리)가 다를 수 있기 때문. 파괴적 작업 전엔 반드시 git fetch 후 원격 ref 기준으로 merge-base를 확인한다.
선형성/무관성 검증 순서
1
2
3
4
5
git fetch origin
# A에 있고 B에 없는 커밋
git log --oneline B..origin/A
# 공통 조상 — 비어 있으면 "완전 무관한 히스토리"(unrelated)
git merge-base origin/A B
merge-base가 비어 있음 → 두 브랜치는 공통 뿌리가 없는 orphan/unrelated 관계. ff 불가, merge도--allow-unrelated-histories필요.merge-base == 한쪽 tip→ 그쪽이 다른쪽의 순수 조상 → ff 가능(충돌·머지커밋 0).
실제로 밟은 함정
- GitHub repo에 버려진 초기
main(자동 생성된Initial commit3개짜리)이 진짜 작업 라인(master계열 450커밋)과 뿌리부터 무관하게 존재. - 로컬
main(진짜 라인의 조상)을 작업본으로 ff →git push origin main이 non-fast-forward로 거부. 원인은 origin/main이 그 고아 브랜치였기 때문. - 무관한 두 히스토리는 ff가 원천적으로 불가 → 교체하려면 원격을
--force하거나 삭제 후 재push.
1
2
3
# 고아 원격 브랜치를 실제 작업본으로 교체 (force 대신 삭제+push가 의도 명확)
git push origin --delete main
git push origin main # 로컬 main(진짜 작업본)으로 새로 생성
default 브랜치 삭제 순서
원격 기본 브랜치(default branch)는 삭제 불가. master→main 전환 시 순서를 지켜야 한다:
1
2
3
git push origin main # 1. 새 default 후보를 먼저 push
gh repo edit --default-branch main # 2. GitHub default 전환
git push origin --delete master # 3. 그 다음에야 구 default 삭제
교훈
- 로컬 브랜치 그래프(
git log --all --graph)는 원격 실체를 반영 안 할 수 있다. 통합·삭제 전git fetch필수. non-fast-forward거부는 대개 “원격이 앞서 있다”지만, 공통 조상이 아예 없는 orphan일 때도 뜬다 —merge-base로 구분.- 파괴적 push 실패는 오히려 안전장치다. 강제로 밀지 말고 원격 상태부터 조사.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.