DaleSchool

rebase 기초

입문25분

학습 목표

  • rebase와 merge의 결과 차이를 시각적으로 이해할 수 있다
  • git rebase로 브랜치를 최신 상태로 업데이트할 수 있다
  • interactive rebase로 커밋 메시지를 수정하거나 합칠 수 있다

동작하는 코드

예제 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 설정에서 허용할 병합 방법을 제한할 수 있습니다.

  1. feature 브랜치를 만들고 커밋 3개를 추가하세요.
  2. main에도 새 커밋을 추가하세요.
  3. feature에서 git rebase main으로 재배치하고 git log --oneline --graph --all로 히스토리를 확인하세요.
  4. 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를 지원하지 않기 때문