브랜치를 병합할 때 같은 파일의 같은 부분을 두 브랜치가 각각 다르게 수정했다면, Git은 어느 쪽을 선택해야 할지 모른다. 이것이 “충돌(conflict)”이다. 충돌은 무섭지 않다 — Git이 판단을 사람에게 넘긴 것일 뿐이다. 판단 기준(브랜치 우선순위, 날짜)을 정해서 체계적으로 선택하면 된다.
1 브랜치 상태 파악
머지를 시도하기 전에, 두 브랜치가 현재 어떤 상태인지 먼저 확인한다.
git branch -a:-a옵션은 로컬 브랜치와 원격 브랜치를 모두 보여준다. 원격 브랜치는remotes/origin/접두사가 붙는다.git status: 현재 브랜치 이름, 스테이징 영역, 작업 디렉토리의 변경 사항을 보여준다.
1.1 브랜치 간 커밋 divergence 확인
두 브랜치가 공통 조상에서 얼마나 갈라졌는지 확인한다.
git fetch origin 을 먼저 실행하는 이유: 원격 저장소의 최신 상태를 로컬에 가져와야 정확한 비교가 가능하다. fetch 없이 비교하면 로컬에 캐시된 오래된 정보를 기준으로 비교하게 된다.
... (triple dot) 의미:
- 두 브랜치의 공통 조상(merge base) 이후 각 브랜치에만 있는 커밋을 보여준다
<기호: 왼쪽 브랜치에만 있는 커밋>기호: 오른쪽 브랜치에만 있는 커밋
Example output:
< e504cfd citation improved <- feat/chatbot에만 있는 커밋
> c968455 Merge branch 'feat/code-analyzer' <- main에만 있는 커밋
두 브랜치가 같은 출발점에서 각자 다른 방향으로 뻗어나간 상태임을 알 수 있다. < 와 > 의 개수가 많을수록 두 브랜치 사이의 차이가 크고, 충돌 가능성도 높아진다.
2 Merge Base (공통 조상)
9ef1d052e8b4407c417f18e19a18edf78e83705c
Merge Base란:
- 두 브랜치가 갈라지기 시작한 마지막 공통 커밋이다
- Git은 머지 시 이 merge base를 기준으로 각 브랜치의 변경사항을 3-way merge한다
A 브랜치 변경+B 브랜치 변경vs공통 조상→ 충돌 여부 판단
비유로 설명하면, 두 사람이 같은 원본 문서를 복사해서 각자 다른 부분을 수정한 상황이다. 원본 문서가 바로 merge base이다. 원본과 비교해야만 “누가 어디를 바꿨는지” 판단할 수 있다.
3 3-way Merge와 충돌의 원리
Git의 merge는 단순히 두 파일을 비교하는 것이 아니다. 세 개의 버전을 비교한다:
- Base (공통 조상 버전) — 두 브랜치가 갈라지기 전 원본
- Ours (현재 브랜치 버전) — HEAD가 가리키는 브랜치의 파일
- Theirs (상대 브랜치 버전) — 머지하려는 브랜치의 파일
왜 2-way가 아닌 3-way인가? 두 파일만 비교하면 “누가 바꿨는지”를 알 수 없다. 예를 들어 A 파일에는 “hello”가 있고 B 파일에는 “world”가 있다면, 어느 쪽이 원본이고 어느 쪽이 수정본인지 판단할 수 없다. 원본(Base)을 함께 보면 판단이 가능하다.
이 세 버전을 비교해서:
- Base와 같고 Ours만 다르면 → Ours를 채택 (현재 브랜치만 수정한 것이므로)
- Base와 같고 Theirs만 다르면 → Theirs를 채택 (상대 브랜치만 수정한 것이므로)
- Ours와 Theirs가 모두 Base와 다르면 → CONFLICT (Git이 결정할 수 없으므로 사람에게 넘김)
| Base | Ours | Theirs | 결과 |
|---|---|---|---|
| A | A | A | 변경 없음 (A 유지) |
| A | B | A | Ours 채택 (B) |
| A | A | C | Theirs 채택 (C) |
| A | B | C | CONFLICT (사람이 결정) |
| A | B | B | 양쪽이 같은 수정 → B 채택 (충돌 아님) |
4 충돌 파일 확인: 머지 시뮬레이션
실제로 머지하기 전에 어떤 파일이 충돌하는지 미리 확인할 수 있다. 이 방법을 사용하면 머지를 커밋하지 않고 충돌 상황만 파악한 뒤 원래 상태로 돌아올 수 있다.
Options:
--no-commit: 머지 후 자동 커밋하지 않는다 → 결과를 검토할 수 있다--no-ff: fast-forward 방지 → 항상 머지 커밋을 생성한다. fast-forward란 한쪽 브랜치가 다른 쪽의 직계 조상인 경우 머지 커밋 없이 포인터만 이동하는 것이다.--no-ff를 쓰면 이를 방지하여 반드시 머지 커밋이 만들어진다
실행 결과:
Auto-merging .gitignore
CONFLICT (content): Merge conflict in .gitignore
Auto-merging data/messages.json
CONFLICT (content): Merge conflict in data/messages.json
Automatic merge failed; fix conflicts and then commit the result.
Auto-merging성공 = 같은 파일이지만 수정 위치가 겹치지 않아 Git이 자동으로 합침CONFLICT (content)= 같은 파일의 같은 부분을 두 브랜치가 각자 다르게 수정 → Git이 어느 쪽을 선택할지 모르는 상태
4.1 충돌 마커
충돌난 파일을 열어보면 이런 형태가 된다:
<<<<<<< HEAD (현재 브랜치의 내용)
현재 브랜치에서 수정한 내용
=======
상대 브랜치에서 수정한 내용
>>>>>>> origin/main
<<<<<<< HEAD부터=======까지: 현재 브랜치(Ours)의 내용이다=======부터>>>>>>> origin/main까지: 상대 브랜치(Theirs)의 내용이다- 이 마커를 수동으로 편집하여 원하는 내용만 남기고 마커를 삭제하면 충돌이 해소된다
4.2 머지 취소
시뮬레이션 후 원래 상태로 돌아가려면:
Switched to branch 'feat/chatbot'
--abort 실행 후 머지 이전 브랜치 상태로 완전히 복원된다.
- 충돌 파일들의
<<<<<<<,=======,>>>>>>>마커 제거 - 브랜치를 머지 시도 이전 상태로 완전 복원
- 머지 중에 발생한 모든 변경을 되돌린다
5 충돌 해소 전략
충돌을 해소하는 방법은 상황에 따라 세 가지 전략으로 나뉜다. 어떤 전략을 선택할지는 “한쪽 브랜치가 명확히 우선인가, 아니면 파일마다 다른가”로 결정한다.
5.1 전략 A: -X ours / -X theirs (전체 파일 일괄)
한쪽 브랜치가 명확히 최신이거나 우선인 경우 사용한다. 예를 들어 main 브랜치에 이미 검증된 코드가 있고, feature 브랜치에서 실험적으로 수정한 것이라면 -X ours 로 현재 브랜치를 일괄 우선할 수 있다.
Merge made by the 'ort' strategy.
.gitignore | 3 +--
data/messages.json | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
충돌이 있던 파일들이 자동으로 현재 브랜치 기준으로 해결되어 머지 커밋이 생성된다.
-X ours :
-X는 merge strategy extension option이다ours: 충돌 발생 시 현재 브랜치(HEAD)의 내용을 자동 선택한다- 충돌이 없는 파일은 정상적으로 양쪽 변경사항 모두 반영한다
- 충돌이 있는 파일만 현재 브랜치 기준으로 덮어쓴다
-X theirs 는 반대로 상대 브랜치를 선택한다.
-X ours vs -s ours — 반드시 구분해야 한다
git merge -s ours origin/main # strategy: 상대 브랜치 변경 전체 무시 (위험!)
git merge -X ours origin/main # extension: 충돌 부분만 현재 브랜치 선택 (안전)-s ours는 상대 브랜치의 모든 변경을 무시한다. 충돌이 없는 파일의 변경도 버린다. 실수로 사용하면 상대 브랜치의 작업이 통째로 사라진다.-X ours는 충돌이 발생한 부분에서만 현재 브랜치를 선택한다. 충돌이 없는 변경은 정상적으로 병합된다.
한 글자 차이(-s vs -X)가 결과를 완전히 바꾸므로 주의해야 한다.
5.2 전략 B: --ours / --theirs (파일별 수동 선택)
파일마다 어느 브랜치가 더 최신인지 다를 때 사용한다. 예를 들어 .gitignore 는 main이 최신이지만, data/messages.json 은 feature 브랜치가 최신인 경우이다.
먼저 --no-commit 으로 머지해서 충돌 상태에 진입한 후, 파일별로 선택한다:
# 충돌 상태 진입
git merge origin/main --no-commit --no-ff
# 현재 브랜치 버전 선택
git checkout --ours data/messages.json
git add data/messages.json
# 상대 브랜치 버전 선택
git checkout --theirs .gitignore
git add .gitignoregit add 를 반드시 해야 하는 이유:
git checkout --ours/--theirs로 파일 내용을 선택하면, 파일은 수정되지만 git 내부적으로는 아직 “충돌 상태”로 표시된다git add를 해야 “이 파일의 충돌을 해소했다”고 git에게 알리는 것이다git add없이git commit을 시도하면 “unmerged files” 에러가 발생한다
확인:
On branch feat/standardizer
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
이 메시지가 나오면 모든 충돌이 해소된 것이다. git commit 으로 머지를 완료한다.
5.3 전략 C: 날짜 기반 파일별 선택
파일마다 어느 브랜치가 더 최신인지 판단해야 하는데, 파일 수가 많을 때 커밋 날짜로 비교하는 기법이다. 충돌 파일이 10개 이상이면 일일이 내용을 비교하기 어려우므로, 날짜를 기준으로 빠르게 판단할 수 있다.
MERGE_BASE=$(git merge-base origin/feat/standardizer origin/main)
for f in .gitignore data/messages.json poetry.lock pyproject.toml; do
std_date=$(git log -1 --format="%ai" "$MERGE_BASE..origin/feat/standardizer" -- "$f")
main_date=$(git log -1 --format="%ai" "$MERGE_BASE..origin/main" -- "$f")
echo "FILE: $f"
echo " standardizer: $std_date"
echo " main: $main_date"
done핵심: MERGE_BASE..브랜치 범위 지정
merge_base..브랜치= merge base 이후 해당 브랜치에서의 변경만 조회한다- merge base 이전 커밋까지 포함하면 잘못된 날짜 비교가 된다
- “이 브랜치에서 독자적으로 수정한 마지막 날짜”를 정확히 보려면 반드시 범위를 제한해야 한다
git log -1은 가장 최근 커밋 1개만 보여주고,--format="%ai"는 날짜를 ISO 형식으로 출력한다-- "$f"는 특정 파일에 대한 커밋만 필터링한다
Example output:
FILE: .gitignore
standardizer: 2026-03-16 14:18:54 +0900
main: 2026-03-30 10:07:46 +0900 <- main이 더 최신
FILE: data/messages.json
standardizer: 2026-03-06 05:53:47 +0000 <- standardizer가 더 최신
main: 2026-02-23 08:54:39 +0900
날짜 비교 결과에 따라 파일별로 --ours 또는 --theirs 를 선택한다. 위 예시에서는 .gitignore 는 main이 더 최신이므로 --theirs (상대 브랜치)를, data/messages.json 은 standardizer가 더 최신이므로 --ours (현재 브랜치)를 선택하면 된다.
날짜가 출력되지 않는 파일이 있을 수 있다. 이는 해당 브랜치에서 merge base 이후 그 파일을 한 번도 수정하지 않았다는 뜻이다. 이 경우 상대 브랜치의 버전을 선택하면 된다 — 한쪽만 수정했으므로 수정한 쪽이 최신이다.
6 전체 워크플로우
머지 충돌 해결의 전체 흐름을 정리한다:
[GitHub PR 충돌 발생]
|
git log A...B --left-right -> 두 브랜치 차이 파악
|
git merge-base A B -> 공통 조상 확인
|
git merge --no-commit --no-ff -> 충돌 파일 목록 확인
|
+---+----+
한 브랜치가 파일마다 다름
명확히 최신 (날짜 비교 필요)
| |
-X ours 로 git log --format="%ai" MERGE_BASE..브랜치 -- 파일
한 번에 해결 로 날짜 비교 후
파일별 --ours / --theirs 선택
+ git add
+---+----+
|
git commit
|
git push
|
GitHub PR 충돌 해소 확인
7 핵심 명령어 레퍼런스
| 명령어/개념 | 설명 |
|---|---|
git merge-base A B |
두 브랜치의 공통 조상 커밋 해시를 출력한다 |
git log A...B --left-right |
두 브랜치 간 diverge된 커밋을 비교한다 |
git merge --no-commit --no-ff |
머지 시뮬레이션 (충돌 파일 확인용) |
git merge --abort |
진행 중인 머지를 전체 취소한다 |
git merge -X ours |
충돌 시 현재 브랜치를 자동 선택한다 |
git merge -X theirs |
충돌 시 상대 브랜치를 자동 선택한다 |
git checkout --ours <file> |
특정 파일만 현재 브랜치 버전으로 선택한다 |
git checkout --theirs <file> |
특정 파일만 상대 브랜치 버전으로 선택한다 |
git log -1 --format="%ai" A..B -- <file> |
특정 파일의 브랜치 내 최신 커밋 날짜를 조회한다 |
8 관련 주제
- Git Merge vs Rebase — merge와 rebase의 차이
- Git 머지 충돌 해결: 실전 케이스 — 이 개념들을 적용한 실전 사례 4가지
- Git 브랜치 동기화 — 로컬 브랜치를 원격에 강제 동기화
- Git Stash — stash 상세 사용법