3-Way Merge: 갈라진 브랜치를 합치는 원리

공통 조상을 기준으로 두 브랜치를 비교하는 3-way merge의 작동 원리, 자동 병합이 성공하는 조건, 충돌이 발생하는 이유를 이해한다.

· 4 min read · PALDYN Team

지난 글에서 직선 히스토리의 fast-forward merge를 살펴봤다. 두 브랜치가 서로 다른 커밋을 가지고 갈라진 경우엔 3-Way Merge가 필요하다. Git이 어떻게 두 브랜치의 변경사항을 자동으로 합치는지, 그리고 왜 충돌이 생기는지 이해해보자.

왜 “3”인가

단순히 두 파일을 비교하는 2-way diff로는 문제가 있다. main이 함수를 삭제했고 feature도 같은 함수를 수정했다면, Git은 “삭제”인지 “의도적으로 다르게 쓴 것”인지 구분할 수 없다. 세 번째 기준인 **공통 조상(Base)**이 있으면 무엇이 변경된 것인지 명확해진다.

3-Way Merge의 세 스냅샷:

  1. Base — 두 브랜치가 갈라진 공통 조상 커밋
  2. Ours — 현재 브랜치(HEAD)의 최신 커밋
  3. Theirs — 병합 대상 브랜치의 최신 커밋

3-Way Merge 원리

자동 병합 규칙

Git은 파일의 각 “덩어리(hunk)“를 세 버전과 비교한다.

BaseOursTheirs결과
AABB (Theirs가 변경)
ABAB (Ours가 변경)
ABBB (둘 다 같은 변경)
ABC충돌 (둘 다 다르게 변경)
AAAA (아무도 안 변경)

핵심은 “누가 변경했는가”를 Base와 비교해서 판단한다는 것이다.

실제 명령

git switch main
git merge feature
# Merge made by the 'ort' strategy.
#  feature.js | 18 ++++++++++++++++++
#  1 file changed, 18 insertions(+)

fast-forward와 달리 출력에 Fast-forward 대신 Merge made by 메시지가 나타난다. 이때 머지 커밋이 생성된다.

머지 커밋의 특징

git log --oneline --graph

# *   f3a1b2c Merge branch 'feature'
# |\
# | * e4d5c6b feature: 사용자 프로필 추가
# | * a1b2c3d feature: API 연동
# * | 9f8e7d6 main: 버그 수정
# |/
# * 7c6b5a4 공통 조상 커밋

머지 커밋은 두 개의 부모를 가진다. git log에서 |/ 모양으로 두 선이 합쳐지는 지점이 머지 커밋이다.

자동 병합 vs 충돌

자동 병합과 충돌 비교

같은 파일도 수정한 위치가 다르면 자동 병합된다. 두 브랜치가 동일한 줄을 서로 다르게 수정했을 때만 충돌(CONFLICT)이 발생한다.

충돌 발생 시 흐름

git merge feature
# CONFLICT (content): Merge conflict in app.js
# Automatic merge failed; fix conflicts and then commit the result.

# 충돌 파일 수정 후
git add app.js
git merge --continue   # 또는 git commit

충돌을 해결하고 git add로 스테이징한 뒤 git merge --continue로 머지 커밋을 완성한다. 병합 자체를 취소하려면 git merge --abort를 쓴다.

공통 조상 찾기

# 두 브랜치의 공통 조상 커밋 해시 확인
git merge-base main feature
# 7c6b5a4...

Git이 내부적으로 이 커밋을 Base로 사용해 3-way diff를 계산한다.


지난 글: Fast-Forward Merge: 직선 히스토리로 브랜치 병합하기

다음 글: —no-ff 옵션: 머지 커밋을 항상 남기는 이유


읽어주셔서 감사합니다. 😊