일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 함수
- 데이터베이스
- 함수 선언
- 웹브라우저 수용도
- 배열 3요소
- 자바 오류
- 자바
- 예외
- 숫자형식오류
- SQL입문
- 예제
- 크롤링 오류
- SQL
- 크롤링
- 키-값 데이터베이스
- HTML역사
- dbms
- DoIt
- DoitSQL
- 배열 예제
- html
- 생성자
- Doit입문SQL
- 숫자 형식
- 자바 예외
- DoitSQL입문
- 우아한테크
- 페이지분석
- 웹 브라우저 전쟁
- R1C3
- Today
- Total
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 함수
- 데이터베이스
- 함수 선언
- 웹브라우저 수용도
- 배열 3요소
- 자바 오류
- 자바
- 예외
- 숫자형식오류
- SQL입문
- 예제
- 크롤링 오류
- SQL
- 크롤링
- 키-값 데이터베이스
- HTML역사
- dbms
- DoIt
- DoitSQL
- 배열 예제
- html
- 생성자
- Doit입문SQL
- 숫자 형식
- 자바 예외
- DoitSQL입문
- 우아한테크
- 페이지분석
- 웹 브라우저 전쟁
- R1C3
- Today
- Total
프로그래밍
[Git] 왜 사람들은 git을 어렵다고 느낄까?(Merge와 Conflict) 본문
왜 사람들은 git을 어렵다고 느낄까?
git을 사용하면서 어렵다고 느끼는 지점은 "merge를 하려고 할때" 찾아오는것 같다
그럼 왜 Merge를 할때면 git의 작동방식을 이해하기가 힘든지
도대체 conflict(충돌)은 언제 일어나는지
git은 무엇을 기준으로 병합판단을 하는지에 대해서 알아보자
Merge
먼저 Merge는 병합이다
내가 가진 여러개의 브랜치, 거기에 따른 커밋기록을 다시 하나의 브랜치를 합친다
변경사항을 버전 관리를 하다가 해당 사항들을 합쳐주어서 하나의 버전(소스코드)를 만들 수 있다
보통 여러명이 각각 기능 개발을 하고 완성이 되면 개발 브랜치에 합치는 경우를 예시로 들 수 있다
브랜치 구조
C ← 병합 대상 (ex: feat_a)
/
A — B
\
D ← 현재 브랜치 (ex: main)
main 브랜치
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
main에서 파생된 feat_a 브랜치
LANGUAGE_CODE = 'kr' # 변경
TIME_ZONE = 'UTC'
feat_a -> main 병합 결과
LANGUAGE_CODE = 'kr' # 변경 적용
TIME_ZONE = None
이런 간단한 경우는 쉽게 이해할 수 있을것이다
하지만 브랜치가 많아지고 Conflict가 난다면 복잡해진다
conflict
Conflict는 충돌이다
git이 자동으로 변경사항을 병합하지 못했을 경우 발생한다
주로 같은 지점을 변경할 경우 발생한다
main 브랜치
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
main에서 파생된 feat_a 브랜치
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'ABC' # 변경함
main에서 수정
LANGUAGE_CODE = 'en-us'
TIME_ZONE = None # 변경함
수정된 main 브랜치와 feat_a 브랜치 병합시도
-> 충돌 발생
같은 곳을 다른 내용으로 수정했기에 git은 "둘 중에 뭐가 맞아?"라고 하며 충돌이 발생하게 된다
만약 다른 줄을 수정했다면 충돌은 발생하지 않았을 것이다
상황 | Git 판단 | 결과 |
양쪽 모두 변경 안함 | 그대로 둠 | ✅ 유지 |
한쪽만 변경 | 변경한 쪽 반영 | ✅ 자동 병합 |
양쪽 모두 다르게 변경 | 충돌 발생 | ⚠️ 수동 해결 필요 |
Target 브랜치와 Source 브랜치
Target 브랜치는 우리가 흔히 말하는 main이고
Source 브랜치는 feature 브랜치가 될것이다
병합은 Target 브랜치에 Source 브랜치의 변경사항을 적용한다
그럼 두 브랜치가 바뀌면 병합의 결과가 달라질까?
-> 달라지지 않는다
git은 변경한쪽의 내용을 따른다
이때 변경한 브랜치가 Target 브랜치든 Source 브랜치든 상관없이 변경한쪽의 내용을 무조건 따른다
그렇기에 두 브랜치의 변경사항을 모두 반영할 수 있는것이다
★Merge는 덮어쓰기가 아니다 ★
병합을 하는 원리는 공통 조상을 파악 후 비교하여 변경을 적용하는것이다
만약 덮어쓰기라면 무조건 최신 변경사항을 적용할 것이고
그럼 충돌이 날 일은 없을 것이다
Merge 예시
브랜치가 여러개 파생된 예시를 보고 Merge의 원리를 이해해보자
브랜치 구조
main 브랜치
A — B — C — D(main 브랜치 HEAD)
사용자 E 브랜치 (B에서 시작)
↘︎
E(feature 브랜치)
다른 사용자가 최신 변경 사항(D)을 반영하지 않고 과거 시점(B)에서 브랜치를 파서 작업한 경우이다
꽤 자주 발생할 수 있는 상황이다
main 브랜치의 Head인 D와 사용자 E 브랜치를 병합한다면 어떻게 될까?
(보통은 과거 시점 브랜치를 보유한 작업자가 pull을 받아 충돌을 해결 후 다시 Merge하는 방법을 추천)
먼저 두 브랜치의 공통 조상은 main 브랜치의 B시점이다
해당 시점을 기준으로 비교하여 병합이 실행될것이다
main 브랜치 B
A = 1
B = 2
C = 3
main 브랜치 C
A = 1 # 변경 없음
B = 2 # 변경 없음
C = 4 # C 변경
main 브랜치 D
A = 1 # 변경 없음
B = 3 # B 변경
C = 4 # C는 C 브랜치에서 이미 바뀐 걸 그대로 유지
B시점에서 파생된 브랜치 E
A = 0 # A 변경
B = 2 # 변경 없음
C = 3 # 변경 없음 (즉, B의 상태와 동일)
D = 10 # 새 항목 추가
병합 판단 기준
공통 조상(B)에서 무엇이 변경되었는지를 기준으로 판단한다
항목 | B(공통 조상) | D 브랜치 | E 브랜치 | 병합결과 |
A | 1 | 1 (변경 X) | 0 (변경 O) | ✅ 0 ← E만 변경 |
B | 2 | 3 (변경 O) | 2 (변경 X) | ✅ 3 ← D만 변경 |
C | 3 | 4 (변경 O) | 3 (변경 X) | ✅ 4 ← D만 변경 |
D | - | - | 10 (추가) | ✅ 10 ← E만 추가 |
최종 병합 결과
A = 0 # E에서만 변경 → 반영됨
B = 3 # D에서만 변경 → 반영됨
C = 4 # D에서만 변경 → 반영됨
D = 10 # E에서 새로 추가됨 → 반영됨
만약 단순 덮어쓰기였다면 Source 브랜치인 E 브랜치의 내용이 우선 반영되고
E브랜치가 갖고 있지 않은 브랜치 C, D의 변경사항은 날라갔을 것이다
하지만 공통 조상 기준으로 비교하여 반영하므로 브랜치 C, D의 변경사항 또한 반영되었다
공통 조상 (Base) 현재 브랜치 (HEAD) 병합 대상 (Other)
B D E
--------- --------- ---------
A = 1 A = 1 A = 0 ← 변경됨
B = 2 B = 3 ← 변경됨 B = 2
C = 3 C = 4 ← 변경됨 C = 3
(없음) (없음) D = 10 ← 추가됨
A
│
B ← 공통 조상 (Merge 기준점)
/ \
/ \
C E
\ /
D ← 현재 main HEAD (여기에 merge E)
정리
- Merge는 덮어쓰기가 아니다
- Merge의 기준은 가장 최신의 공통 조상이다
- 같은 곳의 변경이 동시에 일어날 경우 Conflict가 발생한다