[GitHub 100일 챌린지] Day 47 - 3-Way Merge
[GitHub 100일 챌린지] Day 47 - 3-Way Merge
100일 챌린지 Day 47 - Fast-Forward가 불가능할 때 사용하는 3-Way 병합을 배웁니다.
배울 내용
- 3-Way Merge란
- 3-Way Merge 실습
- Merge Commit 이해하기
Topic1. 3-Way Merge란
Fast-Forward가 불가능할 때 Git이 사용하는 병합 방식입니다.
3-Way Merge의 개념
언제 필요한가?:
gitGraph
commit id: "C1"
commit id: "C2"
branch feature
checkout feature
commit id: "C3"
checkout main
commit id: "C4"
checkout feature
commit id: "C5"
checkout main
merge feature tag: "3-Way!"
Fast-Forward 불가능한 상황:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
초기:
C1 ← C2
↑
main
브랜치 생성:
C1 ← C2
↑
main, feature
feature에서 작업:
C1 ← C2 ← C3 ← C5
↑ ↑
main feature
main에서도 작업! (Fast-Forward 불가!)
C3 ← C5 (feature)
↗
C1 ← C2
↘
C4 (main)
❌ Fast-Forward 불가능!
✅ 3-Way Merge 필요!
3-Way Merge의 원리
3개의 커밋을 비교:
1
2
3
4
5
6
7
1. 공통 조상 (Base): C2
2. main의 최신 커밋: C4
3. feature의 최신 커밋: C5
Git이 자동으로 병합:
C2 → C4의 변경사항
C2 → C5의 변경사항
병합 결과:
gitGraph
commit id: "C1"
commit id: "C2"
branch feature
checkout feature
commit id: "C3"
checkout main
commit id: "C4"
checkout feature
commit id: "C5"
checkout main
merge feature tag: "M (Merge Commit)"
Merge Commit 생성:
1
2
3
4
5
C3 ← C5
↗ ↘
C1 ← C2 ← C4 ← M (병합 커밋)
↑
main
해보기: 3-Way Merge 조건 만들기
시나리오: 두 브랜치가 동시에 진행
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 프로젝트 초기화
mkdir three-way-demo
cd three-way-demo
git init
# 초기 커밋
echo "# Project" > README.md
git add README.md
git commit -m "C1: Initial commit"
echo "v1.0" > VERSION
git add VERSION
git commit -m "C2: Add version"
# 현재 상태
git log --oneline
# C2 Add version
# C1 Initial commit
# feature 브랜치 생성
git checkout -b feature/docs
# feature에서 작업
echo "# Documentation" > DOCS.md
git add DOCS.md
git commit -m "C3: Add docs"
echo "# Installation" >> DOCS.md
git add DOCS.md
git commit -m "C5: Update docs"
# main으로 돌아가기
git checkout main
# main에서도 작업 (중요!)
echo "# License: MIT" > LICENSE
git add LICENSE
git commit -m "C4: Add license"
# 브랜치 그래프 확인
git log --oneline --all --graph
# 출력:
* C4 (HEAD -> main) Add license
| * C5 (feature/docs) Update docs
| * C3 Add docs
|/
* C2 Add version
* C1 Initial commit
# ✅ 3-Way Merge 조건 충족!
결과
3-Way Merge 필요 조건:
1
2
3
4
✅ main과 feature가 분기됨
✅ 두 브랜치 모두 새 커밋이 있음
✅ Fast-Forward 불가능
✅ 공통 조상 커밋이 존재 (C2)
Fast-Forward vs 3-Way 비교:
1
2
3
4
5
6
7
8
9
Fast-Forward:
main → C2
feature → C2 → C3 → C4
→ main을 C4로 이동만 하면 됨
3-Way Merge:
main → C2 → C4
feature → C2 → C3 → C5
→ C4와 C5를 병합해야 함!
Topic2. 3-Way Merge 실습
실제로 3-Way Merge를 수행해봅니다.
기본 3-Way Merge
병합 명령어:
1
2
3
4
5
6
7
8
9
10
11
12
13
# main으로 전환
git checkout main
# feature 브랜치 병합
git merge feature/docs
# 출력:
Merge made by the 'recursive' strategy.
DOCS.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 DOCS.md
# "recursive strategy" → 3-Way Merge!
병합 후 상태:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 히스토리 확인
git log --oneline --graph
# 출력:
* M (HEAD -> main) Merge branch 'feature/docs'
|\
| * C5 (feature/docs) Update docs
| * C3 Add docs
* | C4 Add license
|/
* C2 Add version
* C1 Initial commit
# 병합 커밋 M이 생성됨!
그래픽 구조:
1
2
3
C3 ← C5 (feature/docs)
↗ ↘
C1 ← C2 ← C4 ← M (main, HEAD)
Merge Commit의 특징
두 개의 부모 커밋:
1
2
3
4
5
6
7
8
9
10
11
12
# 병합 커밋 상세 정보
git show M
# 출력:
commit M (부모1: C4, 부모2: C5)
Merge: C4 C5
Author: Your Name
Date: ...
Merge branch 'feature/docs'
# 두 부모를 가진 특별한 커밋!
병합 커밋 메시지 커스터마이징:
1
2
3
4
5
6
# 병합 시 메시지 직접 입력
git merge feature/docs -m "Merge: Add documentation to main"
# 에디터로 작성
git merge feature/docs
# (에디터가 열림)
해보기: 완전한 3-Way Merge 워크플로우
실무 시나리오: 팀 협업 상황
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# === 프로젝트 설정 ===
mkdir team-project
cd team-project
git init
echo "# Team App v1.0" > README.md
git add README.md
git commit -m "Initial release"
# === 개발자 A: 검색 기능 ===
git checkout -b feature/search
cat > search.js << 'EOF'
function search(query) {
const results = database.filter(item =>
item.name.includes(query)
);
return results;
}
EOF
git add search.js
git commit -m "feat: Add search functionality"
cat > search.test.js << 'EOF'
describe('Search', () => {
test('finds matching items', () => {
expect(search('test')).toBeDefined();
});
});
EOF
git add search.test.js
git commit -m "test: Add search tests"
# === 개발자 B: 알림 기능 (동시 개발!) ===
git checkout main
git checkout -b feature/notification
cat > notify.js << 'EOF'
function notify(message) {
const notification = new Notification({
title: 'Team App',
body: message
});
notification.show();
}
EOF
git add notify.js
git commit -m "feat: Add notification system"
# === main에 핫픽스 ===
git checkout main
echo "v1.0.1" > VERSION
git add VERSION
git commit -m "chore: Bump version to 1.0.1"
# === 현재 상태 확인 ===
git log --oneline --all --graph
# 출력:
* H1 (HEAD -> main) chore: Bump version to 1.0.1
| * N1 (feature/notification) feat: Add notification system
|/
| * S2 (feature/search) test: Add search tests
| * S1 feat: Add search functionality
|/
* I1 Initial release
# === 검색 기능 병합 (3-Way!) ===
git checkout main
git merge feature/search
# 출력:
Merge made by the 'recursive' strategy.
search.js | 5 +++++
search.test.js | 5 +++++
2 files changed, 10 insertions(+)
# === 알림 기능 병합 (3-Way!) ===
git merge feature/notification
# 출력:
Merge made by the 'recursive' strategy.
notify.js | 7 +++++++
1 file changed, 7 insertions(+)
# === 최종 히스토리 ===
git log --oneline --graph
# 출력:
* M2 (HEAD -> main) Merge branch 'feature/notification'
|\
| * N1 (feature/notification) feat: Add notification system
* | M1 Merge branch 'feature/search'
|\ \
| * | S2 (feature/search) test: Add search tests
| * | S1 feat: Add search functionality
| |/
* | H1 chore: Bump version to 1.0.1
|/
* I1 Initial release
# 두 개의 병합 커밋 M1, M2 생성!
결과
3-Way Merge의 특징:
gitGraph
commit id: "I1: Initial"
branch feature/search
checkout feature/search
commit id: "S1: Search"
commit id: "S2: Tests"
checkout main
commit id: "H1: Hotfix"
merge feature/search tag: "M1"
branch feature/notification
checkout feature/notification
commit id: "N1: Notify"
checkout main
merge feature/notification tag: "M2"
병합 커밋의 역할:
1
2
3
4
✅ 두 브랜치 히스토리 보존
✅ 누가 언제 무엇을 병합했는지 명확
✅ 되돌리기 쉬움 (병합 커밋만 revert)
✅ 브랜치 작업 이력 추적 가능
Topic3. Merge Commit 이해하기
Merge Commit은 특별한 커밋입니다.
Merge Commit의 구조
일반 커밋 vs 병합 커밋:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 일반 커밋
git show C4
# 출력:
commit C4
Author: ...
Date: ...
Add license
diff --git a/LICENSE b/LICENSE
+# License: MIT
# 병합 커밋
git show M
# 출력:
commit M
Merge: C4 C5 ← 두 개의 부모!
Author: ...
Date: ...
Merge branch 'feature/docs'
부모 커밋 확인:
1
2
3
4
5
6
7
8
# 첫 번째 부모 (main의 커밋)
git show M^1
# 두 번째 부모 (feature의 커밋)
git show M^2
# 모든 부모
git log M^1..M^2
Merge Commit 메시지 작성
기본 메시지:
1
2
3
4
git merge feature/login
# 자동 생성 메시지:
Merge branch 'feature/login'
권장 메시지 형식:
1
2
3
4
5
6
7
8
9
git merge feature/login -m "Merge: Add user login feature
- Implement login form with validation
- Add authentication API integration
- Include unit and integration tests
- Update documentation
Reviewed-by: Tech Lead
Tested-by: QA Team"
에디터로 작성:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# --no-ff로 에디터 열기
git merge --no-ff feature/payment
# 에디터에서:
Merge: Integrate payment system
## Changes
- Stripe payment integration
- Payment history tracking
- Receipt generation
## Testing
- [x] Unit tests (95% coverage)
- [x] Integration tests
- [x] Manual testing in staging
## Deployment Notes
- Requires STRIPE_API_KEY environment variable
- Database migration needed
Closes #123
해보기: Merge Commit 분석
시나리오: 병합 커밋 상세 분석
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 병합 상황 만들기
mkdir merge-analysis
cd merge-analysis
git init
echo "base" > file.txt
git add file.txt
git commit -m "Base commit"
# 브랜치 A
git checkout -b branch-a
echo "feature A" >> file.txt
git add file.txt
git commit -m "Add feature A"
# 브랜치 B
git checkout main
git checkout -b branch-b
echo "feature B" >> file.txt
git add file.txt
git commit -m "Add feature B"
# 병합
git checkout main
git merge branch-a
git merge branch-b
# === 병합 커밋 분석 ===
# 1. 병합 커밋 찾기
git log --merges --oneline
# 출력:
M2 Merge branch 'branch-b'
M1 Merge branch 'branch-a'
# 2. 병합 커밋 상세 정보
git show M1
# 출력:
commit M1
Merge: base-commit feature-a-commit
Author: ...
Date: ...
Merge branch 'branch-a'
# 3. 부모 커밋 확인
git log M1^1..M1^2 --oneline
# 출력: branch-a의 커밋들
# 4. 병합으로 들어온 변경사항
git diff M1^1 M1
# 출력: feature A 변경사항
# 5. 병합 커밋만 보기
git log --first-parent --oneline
# 출력:
M2 Merge branch 'branch-b'
M1 Merge branch 'branch-a'
base Base commit
Merge Commit 되돌리기
병합 커밋 revert:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 병합 커밋 되돌리기
git revert -m 1 M
# -m 1: 첫 번째 부모 (main)를 유지
# -m 2: 두 번째 부모 (feature)를 유지
# 예시
git checkout main
git merge feature/experimental
# 문제 발생! 병합 취소
git revert -m 1 HEAD
# feature의 모든 변경사항이 제거됨
병합 전으로 완전히 되돌리기:
1
2
3
4
5
6
7
# 병합 전 커밋으로 reset
git reset --hard HEAD^
# 또는 병합 전 커밋 SHA로
git reset --hard C4
# ⚠️ 주의: push한 경우 사용 금지!
결과
Merge Commit의 특징 정리:
1
2
3
4
5
6
7
8
9
10
11
12
13
구조:
✅ 두 개의 부모 커밋을 가짐
✅ 브랜치 히스토리 보존
✅ 병합 시점과 이유 기록
장점:
✅ 전체 히스토리 추적 가능
✅ 기능 단위 되돌리기 쉬움
✅ 누가 언제 병합했는지 명확
단점:
❌ 히스토리가 복잡해질 수 있음
❌ 그래프가 지저분해질 수 있음
병합 커밋 활용 팁:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 의미있는 병합 메시지 작성
git merge feature/X -m "Merge: Add feature X
- 주요 변경사항 설명
- 리뷰 정보
- 테스트 결과"
# 2. 병합 전 리뷰 확인
git diff main...feature/X
# 3. 병합 후 검증
git log --first-parent
git show HEAD
# 4. 필요시 되돌리기
git revert -m 1 HEAD
정리
오늘 배운 내용:
1. 3-Way Merge:
- Fast-Forward 불가능할 때 사용
- 공통 조상, main, feature 3개 커밋 비교
- 자동으로 병합 커밋 생성
2. 3-Way Merge 실습:
1
2
3
4
5
git checkout main
git merge feature/branch
# 출력: "recursive strategy"
# → 3-Way Merge 수행됨
3. Merge Commit:
- 두 개의 부모 커밋
- 브랜치 히스토리 보존
- 의미있는 메시지 작성 중요
4. Merge Commit 관리:
1
2
3
4
5
6
7
8
9
# 병합 커밋 보기
git log --merges
# 부모 커밋 확인
git show M^1
git show M^2
# 되돌리기
git revert -m 1 M
다음 단계: Day 48에서 Merge Conflict를 배웁니다.
완료 체크:
- 3-Way Merge의 개념을 이해했다
- 3-Way Merge를 수행할 수 있다
- Merge Commit의 구조를 안다
- Merge Commit을 관리할 수 있다
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
