GitEasy#46
Git rebase와 merge의 차이점은 무엇이며 언제 각각을 사용하나요?
#Git#merge#rebase#협업
힌트
커밋 히스토리의 선형성과 협업 컨텍스트를 생각해보세요.
정답 및 해설
Git rebase와 merge의 차이점은 무엇이며 언제 각각을 사용하나요?
Git에서 두 브랜치를 통합하는 방법은 크게 merge와 rebase 두 가지가 있습니다. merge는 브랜치의 히스토리를 보존하는 방식이고, rebase는 커밋을 다른 베이스 위에 재적용하여 선형 히스토리를 만드는 방식입니다. 두 방법의 결과는 코드 내용 면에서 동일하지만, 커밋 히스토리 구조가 완전히 다르므로 팀 상황과 브랜치 유형에 따라 적절히 선택해야 합니다.
Git Merge
동작 원리
merge는 두 브랜치의 최신 커밋과 공통 조상 커밋을 기반으로 3-way merge를 수행합니다. Fast-forward가 가능한 경우 머지 커밋 없이 포인터만 이동하고, 그렇지 않으면 새로운 머지 커밋이 생성됩니다.
# feature 브랜치를 main에 merge
git checkout main
git merge feature
# Fast-forward merge (main이 feature 기반일 때)
# 머지 커밋 없이 main 포인터만 이동
# Fast-forward 방지 (항상 머지 커밋 생성)
git merge --no-ff feature
# 스쿼시 머지 (feature의 모든 커밋을 하나로 합쳐서 머지)
git merge --squash feature
git commit -m "feat: 새로운 기능 추가"
merge 히스토리 예시
merge 전:
A---B---C (feature)
/
D---E---F (main)
merge 후 (--no-ff):
A---B---C (feature)
/ \
D---E---F-----------G (main) ← 머지 커밋 G 생성
Fast-forward merge:
D---E---F---A---B---C (main/feature) ← 포인터만 이동
merge 장단점
# 장점
# 1. 원래 브랜치 히스토리가 완전히 보존됨
# 2. 어떤 브랜치에서 왔는지 맥락 유지
# 3. 공유 브랜치에서 안전 (히스토리 변경 없음)
# 4. 충돌 해결 기록도 히스토리에 남음
# 단점
# 1. 머지 커밋이 많아지면 git log가 복잡해짐
# 2. 비선형 히스토리로 가독성 저하
# 3. git bisect 등 히스토리 탐색이 어려울 수 있음
# git log 시각화
git log --oneline --graph --all
# * bc1d4e7 (main) Merge branch 'feature'
# |\
# | * a3f8c2e feat: 로그인 기능 추가
# | * 7d2b1a3 feat: 회원가입 폼 구현
# |/
# * e5c9f1d chore: 프로젝트 초기화
Git Rebase
동작 원리
rebase는 현재 브랜치의 커밋들을 다른 브랜치의 최신 커밋 위에 재적용합니다. 기존 커밋은 삭제되고 새로운 커밋 해시로 재생성됩니다.
# feature 브랜치를 main 위에 rebase
git checkout feature
git rebase main
# 이후 main에서 fast-forward merge 가능
git checkout main
git merge feature # 자동으로 fast-forward
# 또는 통합하여:
git rebase main feature # feature를 main 위에 rebase
rebase 히스토리 예시
rebase 전:
A---B---C (feature)
/
D---E---F (main)
rebase 후:
A'--B'--C' (feature) ← 새 해시의 커밋으로 재생성
/
D---E---F (main)
fast-forward merge 후:
D---E---F---A'--B'--C' (main) ← 완벽한 선형 히스토리
Interactive Rebase (커밋 정리)
# 최근 3개 커밋을 대화형으로 수정
git rebase -i HEAD~3
# 에디터에서:
# pick a1b2c3 feat: 기능 추가
# pick d4e5f6 fix: 오타 수정
# pick g7h8i9 fix: 또 오타 수정
# 아래와 같이 수정 가능:
# pick a1b2c3 feat: 기능 추가
# squash d4e5f6 fix: 오타 수정 ← 앞 커밋에 합치기
# squash g7h8i9 fix: 또 오타 수정 ← 앞 커밋에 합치기
# 결과: 하나의 깔끔한 커밋만 남음
# rebase 중 충돌 처리
git rebase main
# CONFLICT: 충돌 발생
# 1. 충돌 해결
git add .
git rebase --continue # 다음 커밋으로 진행
# 또는 중단
git rebase --abort # rebase 전 상태로 복구
# 특정 커밋 건너뛰기
git rebase --skip
rebase 장단점
장점:
1. 선형 히스토리 - git log가 깔끔하게 보임
2. git bisect로 버그 찾기 쉬움
3. 불필요한 머지 커밋 없음
4. 코드 리뷰 시 변경사항 파악 용이
단점:
1. 커밋 해시가 변경됨 (실제로 새 커밋 생성)
2. 공유 브랜치에 사용 시 팀원 작업 혼란 초래
3. 히스토리 변조 - 어떤 브랜치에서 왔는지 맥락 손실
4. 복잡한 충돌 처리 (커밋별로 충돌 해결 필요)
황금 규칙: 공유된 브랜치에는 Rebase 금지
# ❌ 절대 하면 안 되는 작업
# main 브랜치처럼 여러 사람이 사용하는 브랜치에 rebase
git checkout main
git rebase feature # 위험! main의 히스토리가 변경됨
# 팀원 A가 이미 main 브랜치를 pull 받았다면:
# - 팀원 A의 로컬 main과 원격 main의 히스토리가 달라짐
# - git pull 시 충돌 발생
# - 팀원이 같은 커밋을 두 번 작업하는 혼란 발생
# ✅ 안전한 rebase 사용: 로컬 피처 브랜치에서만
git checkout feature/my-feature
git rebase main # 로컬에서만 사용하는 브랜치 → 안전
git push --force-with-lease origin feature/my-feature # push 필요 시
실전 워크플로우
GitHub Flow (단순한 팀)
# 1. 피처 브랜치 생성
git checkout -b feature/user-login
# 2. 작업 및 커밋
git commit -m "feat: 로그인 폼 UI 구현"
git commit -m "feat: 로그인 API 연동"
git commit -m "fix: 에러 메시지 처리"
# 3. PR 생성 전 main 변경사항 반영 (rebase 사용)
git fetch origin
git rebase origin/main
# 4. PR 생성 후 main에 squash merge 또는 rebase merge
# GitHub에서 "Squash and merge" 또는 "Rebase and merge" 선택
Git Flow (복잡한 팀)
# develop에 feature 브랜치 merge (히스토리 보존)
git checkout develop
git merge --no-ff feature/user-login
git branch -d feature/user-login
# release 브랜치를 main에 merge (히스토리 보존)
git checkout main
git merge --no-ff release/1.0.0
git tag v1.0.0
# 핫픽스를 main과 develop 모두에 merge
git checkout main
git merge --no-ff hotfix/critical-bug
git checkout develop
git merge --no-ff hotfix/critical-bug
실전 팁
# pull 시 rebase 사용 (머지 커밋 방지)
git pull --rebase origin main
# 또는 전역 설정
git config --global pull.rebase true
# rebase 전 백업 브랜치 생성
git checkout -b feature/backup # 백업
git checkout feature/my-work
git rebase main # rebase 실행
# git reflog로 rebase 전 상태로 복구 가능
git reflog
# feature/my-work@{1}: 이전 상태
git reset --hard feature/my-work@{1}
merge vs rebase 선택 가이드
# merge를 사용해야 하는 경우:
# - main, develop 같은 공유 브랜치에 통합할 때
# - 브랜치 작업 맥락을 히스토리에 남기고 싶을 때
# - CI/CD 파이프라인의 머지 요청/PR 처리 시
# - 이미 push된 브랜치를 다른 팀원도 사용 중일 때
# rebase를 사용해야 하는 경우:
# - 로컬 피처 브랜치를 push 전에 정리할 때
# - main의 최신 변경사항을 피처 브랜치에 반영할 때
# - 커밋 히스토리를 깔끔하게 유지하고 싶을 때
# - interactive rebase로 커밋 메시지/순서를 정리할 때
정리 표
| 구분 | Merge | Rebase |
|---|---|---|
| 히스토리 모양 | 비선형 (분기점 보존) | 선형 (깔끔) |
| 머지 커밋 | 생성됨 (--no-ff 시) | 생성 안 됨 |
| 커밋 해시 | 변경 없음 | 새 해시로 재생성 |
| 원본 브랜치 흔적 | 보존됨 | 없어짐 |
| 공유 브랜치 사용 | 안전 | 위험 (금지) |
| 충돌 해결 | 한 번 | 커밋마다 |
| 주요 용도 | 피처 → main 통합 | 피처 브랜치 정리 |
| git log 가독성 | 복잡 | 단순 |