08. 병합(Merge)과 충돌(Conflict)

🎯 이 장의 목표
  • 갈라진 브랜치를 다시 하나로 합치는 병합을 이해한다
  • Fast-forward 병합과 3-way 병합의 차이를 그림으로 안다
  • 충돌이 왜 생기는지 이해하고, 직접 해결할 수 있다
  • 병합 후 브랜치를 정리한다

8.1 병합이란?

6장에서 feature 브랜치로 평행 세계를 만들어 작업했습니다. 작업이 끝났으면 이제 그 결과물을 다시 main(줄기)에 합쳐야 합니다. 이것이 병합(Merge)입니다.

CODE
                    (D) ← (E)        ← feature (완성된 기능)
                    ↑
(A) ← (B) ← (C)                      ← main
                    │
                    └── git merge ──► main에 D, E를 합친다
🌳 나무 비유로, 가지(feature)에서 키운 열매를 줄기(main)에 다시 붙이는 작업입니다.

병합의 기본 흐름은 항상 같습니다.

BASH
# 1. "합쳐서 받을" 브랜치로 이동 (대상 = main)
git switch main

# 2. "합쳐 넣을" 브랜치를 지정 (소스 = feature)
git merge feature
📌 핵심
방향이 중요! git merge feature는 "현재 브랜치에 feature를 가져와 합친다"는 뜻입니다. 즉 항상 "받을 쪽으로 먼저 이동한 뒤" 병합합니다. main에 feature를 합치려면 main에서 실행해야 합니다.

8.2 병합 방식 ① — Fast-forward (빨리 감기)

가장 단순한 병합입니다. main이 갈라진 뒤 아무 변화가 없었다면, Git은 그냥 main 포인터를 앞으로 "빨리 감기"만 하면 됩니다.

병합 전 — feature가 main보다 앞서 있고, main은 갈라진 뒤 변화 없음:

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D"
    commit id: "E"

병합 후 — main 포인터가 E까지 "빨리 감기"로 전진 (새 커밋 없음):

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    commit id: "D"
    commit id: "E (main=feature)"
BASH
git switch main
git merge feature
CODE
Updating c3d4e5f..a1b2c3d
Fast-forward
 feature.txt | 1 +
 1 file changed, 1 insertion(+)
  • 새로운 병합 커밋이 생기지 않습니다. main이 feature 위치로 이동할 뿐입니다.
  • 이력이 한 줄로 깔끔하게 유지됩니다.

Fast-forward를 막고 싶다면

기능 단위 이력을 명확히 남기려고, 일부러 병합 커밋을 만들 수도 있습니다.

BASH
git merge --no-ff feature

이러면 fast-forward 가능한 상황에서도 병합 커밋을 강제로 만듭니다. "여기서 feature가 합쳐졌다"는 기록이 남습니다.

8.3 병합 방식 ② — 3-way Merge (3방향 병합)

main도 갈라진 뒤 따로 전진했다면, 단순 빨리 감기로는 합칠 수 없습니다. 양쪽이 모두 변했기 때문입니다. 이때 Git은 세 개의 커밋을 비교해 새 "병합 커밋"을 만듭니다.

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D"
    commit id: "E"
    checkout main
    commit id: "F"
    commit id: "G"
공통 조상 C에서 갈라진 뒤, feature(D·E)와 main(F·G)이 양쪽 모두 전진했습니다.

비교 대상 세 가지:

  1. 공통 조상 커밋 (C) — 두 브랜치가 갈라진 지점
  2. main의 끝 (G)
  3. feature의 끝 (E)
BASH
git switch main
git merge feature

Git은 C를 기준으로 양쪽 변경을 합쳐 새로운 병합 커밋(M)을 만듭니다.

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D"
    commit id: "E"
    checkout main
    commit id: "F"
    commit id: "G"
    merge feature id: "M (병합커밋)"
병합 커밋 M은 부모가 둘(G와 E)인 특별한 커밋입니다.
  • 병합 커밋(M)은 부모가 둘인 특별한 커밋입니다.
  • 병합 시 에디터가 열려 병합 메시지를 작성하게 됩니다 (기본 메시지 그대로 두어도 됨).
💡 팁
공통 조상(merge base)이란? 두 브랜치가 갈라지기 직전의 마지막 공통 커밋입니다. Git은 이 지점을 기준으로 "양쪽이 각각 무엇을 바꿨는지" 계산해 합칩니다.

8.4 충돌(Conflict)은 왜 생길까?

3-way 병합에서 양쪽이 같은 파일의 같은 부분을 다르게 고쳤다면, Git은 "둘 중 무엇이 맞는지" 판단할 수 없습니다. 이때 충돌(Conflict)이 발생하고, Git은 사람에게 결정을 넘깁니다.

CODE
main에서:     greeting.txt 1번 줄 → "Hello World"
feature에서:  greeting.txt 1번 줄 → "Hi there"
              ↓
         어느 게 맞아? → Git: "사람이 정해줘!" (충돌)
📌 핵심
충돌은 오류가 아닙니다. 초보자는 충돌을 보면 겁먹지만, 충돌은 Git이 정상적으로 작동하면서 "안전하게 사람의 판단을 기다리는" 상태일 뿐입니다. 침착하게 해결하면 됩니다. 서로 다른 파일을 고쳤거나, 같은 파일이라도 다른 줄을 고쳤다면 충돌은 나지 않습니다.

8.5 충돌 만들고 해결하기 (실습)

직접 충돌을 만들어 해결해봅시다. 가장 좋은 학습법입니다.

1단계: 충돌 상황 만들기

BASH
# main에서 파일 생성/커밋
git switch main
echo "Hello" > greeting.txt
git add greeting.txt
git commit -m "Add greeting on main"

# feature 브랜치 만들어 같은 줄 수정
git switch -c feature
echo "Hi there" > greeting.txt
git add greeting.txt
git commit -m "Change greeting on feature"

# main으로 돌아가 같은 줄을 다르게 수정
git switch main
echo "Hello World" > greeting.txt
git add greeting.txt
git commit -m "Change greeting on main"

2단계: 병합 시도 → 충돌 발생

BASH
git merge feature
CODE
Auto-merging greeting.txt
CONFLICT (content): Merge conflict in greeting.txt
Automatic merge failed; fix conflicts and then commit the result.

git status를 보면 충돌 파일을 알려줍니다.

BASH
git status
CODE
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   greeting.txt

3단계: 충돌 표시 이해하기

greeting.txt를 열면 다음과 같습니다.

CODE
<<<<<<< HEAD
Hello World
=======
Hi there
>>>>>>> feature
표시의미
<<<<<<< HEAD현재 브랜치(main)의 내용 시작
=======경계선
>>>>>>> feature합쳐 넣는 브랜치(feature)의 내용 끝

위쪽(HEAD ~ =======)이 내 브랜치(main), 아래쪽(======= ~ feature)이 상대 브랜치(feature)입니다.

4단계: 수동으로 충돌 해결

원하는 최종 모습으로 직접 편집합니다. 충돌 표시(<<<, ===, >>>)는 반드시 모두 삭제해야 합니다.

CODE
# 예시 1: main 것을 선택
Hello World

# 예시 2: feature 것을 선택
Hi there

# 예시 3: 둘 다 합치기 (직접 작성)
Hello World and Hi there

5단계: 해결 완료 표시 후 커밋

BASH
git add greeting.txt        # "이 충돌 해결했어요" 표시
git commit                  # 병합 커밋 완성 (메시지는 기본값 OK)
CODE
[main 7a8b9c0] Merge branch 'feature'

🎉 충돌 해결 완료! 이력을 보면 병합 커밋이 생긴 것을 확인할 수 있습니다.

BASH
git log --oneline --graph

8.6 충돌 해결을 돕는 도구들

어느 쪽을 통째로 선택하기

BASH
# 충돌 파일에서 내 브랜치(main) 버전 전체 선택
git checkout --ours greeting.txt

# 상대 브랜치(feature) 버전 전체 선택
git checkout --theirs greeting.txt

git add greeting.txt

병합 자체를 취소하기

해결이 너무 복잡해 "없던 일로" 하고 싶다면:

BASH
git merge --abort

병합 시도 이전의 깨끗한 상태로 완전히 되돌아갑니다. 막막할 때 유용한 탈출 버튼입니다.

머지툴 사용

BASH
git mergetool        # 설정된 시각적 충돌 해결 도구 실행

소스트리에서는 충돌 파일을 우클릭해 "Resolve Conflicts → Launch External Merge Tool"로 좌/우 비교 화면에서 클릭만으로 해결할 수 있습니다. 입문자에게는 텍스트 편집보다 직관적입니다.

8.7 병합 위치 지정과 기준 브랜치

병합 방향을 명확히 다시 정리합니다.

BASH
# 항상: 받을 브랜치로 이동 → 합쳐 넣을 브랜치 지정
git switch main          # 받을 쪽
git merge feature        # 넣을 쪽

특정 커밋이나 다른 브랜치를 기준으로 합칠 수도 있지만, 입문 단계에서는 "main으로 가서 feature를 merge"라는 기본 패턴만 확실히 익히면 됩니다.

8.8 병합 여부 확인과 브랜치 정리

병합이 끝난 브랜치는 보통 삭제합니다(6장).

BASH
# main에 이미 병합된 브랜치 목록 보기
git branch --merged

# 아직 병합 안 된 브랜치 목록
git branch --no-merged

# 병합 끝난 브랜치 안전 삭제
git branch -d feature
💡 팁
git branch --merged로 "이미 합쳐져서 지워도 안전한 브랜치"를 확인한 뒤 정리하면 실수를 줄일 수 있습니다.

8.9 이 장에서 배운 것 (요약)

  • 병합 = 갈라진 브랜치를 다시 합치기. 받을 쪽으로 이동 후 git merge 소스
  • Fast-forward: main이 그대로일 때 포인터만 전진(병합 커밋 없음)
  • 3-way merge: 양쪽 다 전진했을 때 공통 조상 기준으로 병합 커밋 생성
  • 충돌은 오류가 아니라 "사람의 판단을 기다리는 정상 상태"
  • 충돌 해결: 파일 편집 → <<< === >>> 제거 → git addgit commit
  • 탈출구: git merge --abort / 통째 선택: --ours/--theirs
  • git branch --merged로 확인 후 병합된 브랜치 정리

✍️ 확인 문제

  1. Fast-forward 병합과 3-way 병합은 각각 언제 일어나나요?
  2. 충돌 파일의 <<<<<<< HEAD 아래 내용은 어느 브랜치 것인가요?
  3. 병합 도중 도저히 안 되겠어서 처음 상태로 돌리려면?
다음 장에서는 병합과 비슷하지만 이력을 더 깔끔하게 만드는 리베이스(Rebase)를 배웁니다. → 09_리베이스.md