Engineering note · Git

Merge와 Squash Merge는 언제 사용할까

2025년 06월 04일이현수
태그gitgithubmergesquash-mergegit-history

짧은 feature 브랜치, 장기 브랜치, 커밋 추적과 되돌리기 방식에 따라 GitHub Merge와 Squash Merge를 선택하는 기준을 정리한다.

GitHub Pull Request를 병합할 때 Create a merge commitSquash and merge 중 무엇을 선택해야 할지 고민할 때가 있다. 선택 기준은 히스토리를 깔끔하게 보이게 만드는 데 있지 않다. 작업 브랜치를 병합 후에도 이어서 사용할지, 개별 커밋을 보존할 가치가 있는지, 장애가 났을 때 어느 단위로 되돌릴지를 먼저 판단해야 한다.

선택 기준부터 정리

매번 개인 취향으로 선택하면 main의 히스토리 규칙이 일정하지 않게 된다. 팀의 규칙을 만들고 지키는 것이 결국 중요하다.

상황적합한 방식이유
PR이 끝나면 feature 브랜치를 삭제한다Squash MergePR의 변경을 커밋 하나로 정리할 수 있다
리뷰 과정에서 fix, wip 커밋이 많이 생긴다Squash Merge중간 작업 커밋을 main에 남기지 않는다
PR 하나를 하나의 배포·revert 단위로 사용한다Squash Merge생성된 커밋 하나를 되돌리면 된다
같은 브랜치를 여러 차례 base 브랜치에 병합한다Merge기존 브랜치와 base의 조상 관계를 유지한다
feature 브랜치에서 다른 후속 브랜치가 갈라진다Merge커밋 관계를 보존해 후속 브랜치 흐름을 추적하기 쉽다
개별 커밋이 각각 의미 있는 검토 단위다Merge작성 순서와 커밋별 변경을 그대로 남긴다

일반적인 짧은 feature 브랜치 중심의 제품 개발이라면 Squash Merge를 기본으로 두고, 장기 브랜치나 커밋 관계를 보존해야 하는 작업에 Merge를 허용할 수 있다. 반대로 커밋 자체를 리뷰와 배포의 중요한 기록으로 관리한다면 Merge를 기본으로 두는 편이 맞다.

GitHub 저장소 설정에서는 허용할 병합 방식을 제한할 수 있다. 선형 히스토리를 강제하는 branch protection을 사용한다면 merge commit을 사용할 수 없으므로 Squash Merge나 Rebase Merge를 선택해야 한다.

짧은 feature 브랜치는 Squash Merge로 정리하기

작은 기능 하나를 위해 브랜치를 만들고 PR이 끝나면 삭제하는 흐름에서는 개발 중간 커밋을 main에 모두 남길 필요가 없는 경우가 많다.

text
A---B                 main
     \
      C---D---E       feature

C, D, E가 리뷰 반영과 작은 수정으로 나뉜 커밋이라면 Squash Merge는 최종 변경을 합친 새 커밋 S를 main에 추가한다.

text
A---B---S             main
     \
      C---D---E       feature

main에서는 PR 하나가 커밋 하나로 보인다. 배포 후 이 기능 전체를 되돌려야 한다면 squash commit을 직접 revert할 수 있다.

sh
git revert S

대신 C, D, E는 main의 조상이 되지 않는다. 개별 커밋을 기준으로 git bisect를 하거나 특정 중간 변경만 revert하려면 사용할 지점이 줄어든다. Squash Merge를 기본으로 사용하려면 PR 제목과 설명을 최종 커밋 메시지로 써도 될 정도로 정리해야 한다.

중간 커밋 자체가 중요하면 Merge 사용하기

feature 브랜치의 커밋이 각각 독립적으로 이해할 수 있는 작업 단위라면 Merge로 보존할 가치가 있다. GitHub의 Create a merge commit은 feature 커밋을 유지하면서 두 브랜치를 부모로 갖는 merge commit M을 만든다.

text
A---B---------M       main
     \       /
      C---D---E       feature

M은 base 브랜치의 기존 커밋과 feature 브랜치의 마지막 커밋을 부모로 갖는다. main에서 C, D, E까지 도달할 수 있으므로 Git은 feature 브랜치가 병합됐다는 관계를 알 수 있다.

이 방식은 다음 상황에서 유용하다.

  • 기능 구현을 여러 개의 의미 있는 커밋으로 나눠 리뷰한다.
  • 특정 커밋이 문제를 만든 시점을 git bisect로 찾고 싶다.
  • feature 브랜치를 기준으로 다른 브랜치가 이미 파생됐다.
  • release나 integration 브랜치를 장기간 유지하며 반복 병합한다.

반면 리뷰 과정의 사소한 수정까지 모두 main에 남는다. Merge를 사용하려면 브랜치 안에서도 커밋 메시지와 변경 범위를 어느 정도 정리하는 편이 좋다.

장기 브랜치에는 Squash Merge를 피하기

Squash Merge 이후에도 같은 feature 브랜치에서 작업을 계속하면 문제가 생길 수 있다. squash commit S는 main에만 존재하고 원래 브랜치의 C, D, E와 부모 관계가 없다. 두 브랜치의 공통 조상은 여전히 B다.

이 상태에서 같은 feature 브랜치로 새 PR을 만들면 이전에 squash한 커밋이 비교 범위에 다시 나타날 수 있다. 충돌이 있었다면 같은 충돌을 반복해서 해결할 가능성도 커진다. GitHub 공식 문서도 병합 후 계속 사용할 head 브랜치에는 Squash Merge를 권장하지 않는다.

Squash Merge를 사용했다면 기존 브랜치를 삭제하고 최신 main에서 다음 브랜치를 만드는 흐름이 단순하다.

sh
git switch main
git pull --ff-only
git switch -c feature/next-task

같은 브랜치를 유지해야 하는 작업이라면 Merge를 사용해 조상 관계를 보존하는 편이 낫다.

빠른 되돌리기만 보면 Squash Merge가 단순하다

Squash Merge는 PR 전체가 커밋 하나이므로 기능 단위 revert가 단순하다. 하지만 기능 안에서 일부 변경만 되돌리려면 squash commit의 내용을 직접 나누어 처리해야 한다.

Merge는 개별 커밋을 보존하므로 작은 단위의 원인 추적과 revert에 유리하다. 병합 전체를 merge commit으로 되돌릴 때는 mainline 부모를 지정해야 한다.

sh
git revert -m 1 M

따라서 “revert가 쉬운 방식”도 팀이 되돌리는 단위에 따라 다르다.

  • PR 전체를 한 번에 되돌리는 경우가 많다면 Squash Merge가 단순하다.
  • 기능 내부의 특정 커밋만 골라 되돌려야 한다면 Merge가 유리하다.

Git이 두 방식을 다르게 보는 이유

두 방식의 핵심 차이는 커밋 수보다 부모 관계에 있다. Merge는 feature 브랜치의 마지막 커밋을 merge commit의 부모로 기록하지만, Squash Merge는 최종 변경만 복사한 새 커밋을 만든다.

로컬의 git merge --squash도 작업 트리와 인덱스를 병합 결과처럼 만들 뿐 MERGE_HEAD를 기록하지 않는다. 이후 커밋은 일반적인 단일 부모 커밋이 된다.

sh
git switch main
git merge --squash feature
git commit -m "feat: 검색 기능 추가"

이 차이 때문에 Squash Merge 이후에는 feature의 tip이 main의 조상으로 판정되지 않을 수 있다.

sh
git merge-base --is-ancestor feature main
git branch --merged main

변경 내용이 main에 들어갔더라도 로컬 Git의 병합 브랜치 목록에는 feature가 나타나지 않을 수 있다. GitHub의 PR 상태와 Git 커밋 그래프의 조상 관계는 구분해서 봐야 한다.

참고한 페이지

좋아요와 댓글

댓글 남기기

댓글 0개

댓글을 불러오는 중입니다.