동작하는 코드
예제 1: 브랜치가 뒤처진 상황 만들기
git init
echo "v1" > file.txt
git add file.txt
git commit -m "feat: 파일 추가"
# feature 브랜치 생성
git checkout -b feature-work
echo "feature 작업" > feature.txt
git add feature.txt
git commit -m "feat: feature 작업 추가"
main에 새 커밋 추가:
git checkout main
echo "main 추가 작업" >> file.txt
git add file.txt
git commit -m "feat: main에 추가 작업"
히스토리 확인:
git log --oneline --graph --all
출력:
* a1b2c3d (HEAD -> main) feat: main에 추가 작업
| * e4f5g6h (feature-work) feat: feature 작업 추가
|/
* f7g8h9i feat: 파일 추가
예제 2: rebase로 최신화
git checkout feature-work
git rebase main
출력:
Successfully rebased and updated refs/heads/feature-work.
히스토리 확인:
git log --oneline --graph --all
출력:
* j0k1l2m (HEAD -> feature-work) feat: feature 작업 추가
* a1b2c3d (main) feat: main에 추가 작업
* f7g8h9i feat: 파일 추가
히스토리가 선형이 됐습니다. feature가 main 위에 재배치됐습니다.
직접 수정하기
merge vs rebase 결과 비교
같은 상황에서 merge와 rebase의 결과:
시작 상태:
A - B - C (main)
\
D - E (feature)
merge 후:
A - B - C - M (main, merge commit)
\ /
D - E (feature)
rebase 후:
A - B - C (main)
\
D' - E' (feature, 재배치됨)
rebase는 feature의 커밋을 main 위로 재배치합니다. 커밋의 내용은 같지만 해시가 달라집니다 (D → D').
interactive rebase: 커밋 정리
# 여러 임시 커밋 만들기
echo "1" >> work.txt && git add work.txt && git commit -m "wip: 작업 중 1"
echo "2" >> work.txt && git add work.txt && git commit -m "wip: 작업 중 2"
echo "3" >> work.txt && git add work.txt && git commit -m "feat: 기능 완성"
마지막 3개 커밋을 대화형으로 정리:
git rebase -i HEAD~3
편집기가 열립니다:
pick a1b2c3d wip: 작업 중 1
pick e4f5g6h wip: 작업 중 2
pick i7j8k9l feat: 기능 완성
wip 커밋들을 squash로 합치기:
pick a1b2c3d wip: 작업 중 1
squash e4f5g6h wip: 작업 중 2
pick i7j8k9l feat: 기능 완성
저장하면 커밋 메시지 편집기가 다시 열립니다:
# 합쳐진 커밋 메시지 편집
wip: 작업 중 1
wip: 작업 중 2
원하는 메시지로 수정:
wip: 작업 초기 구현
저장하면 3개 커밋이 2개로 합쳐집니다.
rebase 중 충돌 처리
git rebase main # 충돌 발생
출력:
CONFLICT (content): Merge conflict in file.txt
error: could not apply a1b2c3d...
hint: Resolve all conflicts manually...
# 충돌 해결
nano file.txt # 편집
git add file.txt
git rebase --continue # 다음 커밋으로 계속
# 또는 rebase 취소
git rebase --abort
"왜?" — rebase vs merge
두 가지 모두 브랜치를 합치는 방법이지만 히스토리가 다릅니다.
| | merge | rebase | | --------- | -------------- | -------------------- | | 히스토리 | 분기 구조 보존 | 선형으로 깔끔 | | 안전성 | 항상 안전 | 공유 브랜치에선 위험 | | 충돌 | 한 번 | 여러 커밋에서 각각 | | 사용 시점 | PR 머지, 협업 | 개인 브랜치 정리 |
황금 규칙: 이미 push한 공유 브랜치(main, develop)는 rebase하지 마세요!
이유: rebase는 커밋 해시를 바꿉니다. 팀원이 이미 다운로드한 커밋과 달라지면 히스토리가 충돌합니다.
# 안전한 rebase 사용 패턴
# 1. 아직 push하지 않은 로컬 브랜치 정리
git rebase -i HEAD~5
# 2. 로컬 feature를 main에 최신화 (push 전)
git rebase main
# 3. PR 병합은 GitHub에서 (merge commit 또는 squash merge)
흔한 실수
실수 1: push된 브랜치 rebase
git push origin feature-login # 이미 push됨
git rebase main # 커밋 해시 변경
git push # 에러!
# error: failed to push some refs...
# 강제 push (팀원에게 문제 발생!)
git push --force # 절대 공유 브랜치에 하지 마세요!
실수 2: 잘못된 rebase 대상
# 잘못됨: main에서 feature를 rebase
git checkout main
git rebase feature-login # main의 히스토리가 변경됨!
# 올바름: feature에서 main을 rebase
git checkout feature-login
git rebase main
실수 3: interactive rebase 중간에 포기
git rebase -i HEAD~3
# 편집기에서 실수로 잘못 저장
# 언제든 취소 가능
git rebase --abort # 원래 상태로 완전히 복원
심화 학습
interactive rebase 명령어 전체
편집기에서 각 커밋에 쓸 수 있는 명령:
| 명령 | 단축 | 설명 |
| -------- | ---- | ---------------------------------- |
| pick | p | 커밋 유지 (변경 없음) |
| reword | r | 커밋 메시지만 수정 |
| edit | e | 커밋 내용 수정 |
| squash | s | 이전 커밋에 합치기 (메시지도 합침) |
| fixup | f | 이전 커밋에 합치기 (메시지 버림) |
| drop | d | 커밋 삭제 |
| exec | e | 커밋 사이에 셸 명령어 실행 |
git pull --rebase: 더 깔끔한 히스토리
# 기본 git pull = fetch + merge (merge commit 생성)
git pull
# git pull --rebase = fetch + rebase (선형 히스토리)
git pull --rebase
# 기본값으로 설정
git config --global pull.rebase true
팀에서 깔끔한 히스토리를 원한다면 pull.rebase true를 설정하세요.
squash merge: PR 병합 시 정리
PR 병합 방법 세 가지:
# 1. Merge commit: 모든 커밋 보존 + merge commit
git merge --no-ff feature-branch
# 2. Squash merge: 모든 커밋을 하나로 합쳐 main에
git merge --squash feature-branch
git commit -m "feat: feature 기능 통합"
# 3. Rebase merge: 선형으로 main에 붙이기
git rebase main && git checkout main && git merge feature-branch
GitHub에서는 PR 설정에서 허용할 병합 방법을 제한할 수 있습니다.
- feature 브랜치를 만들고 커밋 3개를 추가하세요.
- main에도 새 커밋을 추가하세요.
- feature에서
git rebase main으로 재배치하고git log --oneline --graph --all로 히스토리를 확인하세요. - feature에
wip:커밋 2개와feat:커밋 1개를 추가하고git rebase -i HEAD~3으로 wip 커밋을 squash 하세요.
Q1. rebase를 공유 브랜치(이미 push한 브랜치)에 쓰면 안 되는 이유는?
- A) rebase는 merge보다 느리기 때문
- B) rebase는 커밋 해시를 바꾸므로 다른 사람의 히스토리와 충돌
- C) rebase는 파일을 삭제하기 때문
- D) GitHub가 rebase를 지원하지 않기 때문