콘텐츠로 이동

ADR-0007: 머지 전략 — 경계별 (feature→dev Squash, dev→main Merge commit)

  • 상태: 승인됨
  • 날짜: 2026-06-21
  • 대체: ADR-0004

맥락

ADR-0004는 모든 머지를 Squash로 통일했다. 그러나 dev는 계속 재사용하는 장수 통합 브랜치라, 이를 Squash로 main에 머지하면 Squash가 새 SHA의 커밋을 만들어 dev와 main이 같은 내용·다른 커밋으로 발산한다. 그 결과 릴리스마다 dev를 reset --hard origin/main + force-push로 재정렬해야 하고, main에 브랜치 보호(force-push 금지, "Require linear history")를 걸면 이마저 막힌다.

머지 방식은 경계의 성격에 맞춰야 한다 — 일회용 브랜치(feature/fix)와 장수 브랜치(dev)는 다르다.

결정

경계마다 머지 방식을 다르게 둔다.

feature/* · fix/* → dev: Squash 머지 - PR 하나 = 커밋 하나 = 되돌릴 수 있는 단위. PR 내부 중간 커밋은 dev에 남기지 않는다. - Squash 시 PR 제목이 최종 커밋 메시지가 되므로 Conventional Commits 형식([[0003-branch-strategy]], git-conventions.md)을 정확히 따른다. - 머지 후 feature/fix 브랜치는 삭제한다. 브랜치가 일회용이라 발산이 생기지 않는다.

dev → main: Merge commit (--no-ff), PR 필수 - 릴리스 단위 PR. 리뷰와 CI 게이트(pull_request 이벤트 + required status checks)를 이 PR에서 통과시킨다. - Merge commit은 기존 커밋의 SHA를 공유하므로 dev와 main이 발산하지 않는다 → 동기화용 reset/force-push가 필요 없다. - 머지 커밋이 "이번 릴리스에 무엇이 들어갔는지"를 기록한다. 머지 후 main에 태그한다([[0003-branch-strategy]]).

대안

  • 모든 머지 Squash(ADR-0004) — 장수 dev→main에서 발산을 일으켜 매 릴리스 reset+force-push가 필요하고, main 브랜치 보호와 충돌한다. 그래서 대체했다.
  • dev → main을 ff-only — 선형 히스토리에 발산도 없지만, GitHub UI에는 fast-forward 머지 버튼이 없어 CLI로만 가능하다. 그러면 dev → main의 PR 리뷰·CI 게이트를 포기하게 된다. 우리는 릴리스에 PR 게이트를 원하므로 채택하지 않았다.
  • Rebase 머지 — PR이 여러 커밋으로 흩어져 "PR 단위 롤백"이 어렵고, 재부모화로 SHA가 바뀌어 발산을 일으킨다.

결과

  • dev는 PR당 커밋 하나로 깔끔하게 쌓이고, main은 릴리스 머지 커밋으로 릴리스 경계가 드러난다.
  • dev/main이 발산하지 않아 릴리스 후 동기화 작업(reset/force-push)이 사라진다.
  • CI는 pull_request(머지 전 게이트), CD는 push: main 또는 릴리스 태그로 트리거한다 — 머지 방식과 무관하게 동작한다.
  • 한계: GitHub 저장소의 머지 버튼 설정은 저장소 전역이라 "dev는 Squash·main은 Merge commit"을 버튼만으로 분리 강제할 수 없다. 양쪽 버튼을 모두 켜두고 규약·리뷰로 운영하며, 더 강한 강제가 필요하면 ruleset/머지 큐로 보완한다(규칙 강제 단계).