09. 리베이스(Rebase)

🎯 이 장의 목표
  • 리베이스가 무엇이고 병합과 어떻게 다른지 이해한다
  • 리베이스로 이력을 깔끔하게 정리하는 법을 익힌다
  • 리베이스 충돌을 해결하고 취소하는 법을 안다
  • 리베이스의 황금률(공유된 커밋에 쓰지 말 것)을 반드시 기억한다

9.1 리베이스란?

병합(merge)이 두 갈래를 "합쳐서 새 커밋을 만드는" 것이라면, 리베이스(rebase)는 "내 브랜치의 출발점(base)을 옮겨 붙이는 것"입니다. 이름 그대로 re-base(기반을 다시 잡다)입니다.

리베이스 전 — feature는 C에서 갈라져 있고, main은 G까지 전진:

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D"
    commit id: "E"
    checkout main
    commit id: "F"
    commit id: "G"

리베이스 후 — feature의 출발점이 C에서 G로 옮겨져, D·E가 G 뒤에 새 커밋(D'·E')으로 다시 쌓임:

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    commit id: "F"
    commit id: "G"
    branch feature
    checkout feature
    commit id: "D'"
    commit id: "E'"

feature의 커밋들(D, E)을 떼어내서, main의 최신 커밋(G) 뒤에 새 커밋(D', E')으로 다시 쌓습니다. 결과적으로 이력이 한 줄로 곧게 펴집니다.

9.2 리베이스 vs 병합 — 무엇이 다른가?

같은 두 브랜치를 합칠 때, 결과 이력의 모양이 다릅니다.

병합(merge)의 이력

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D"
    commit id: "E"
    checkout main
    commit id: "F"
    commit id: "G"
    merge feature id: "M"
  • 갈라지고 합쳐진 실제 역사가 그대로 보존됩니다.
  • 병합 커밋이 추가되어 이력이 "그물망"처럼 됩니다.

리베이스(rebase)의 이력

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    commit id: "F"
    commit id: "G"
    commit id: "D'"
    commit id: "E'"
  • 마치 처음부터 순서대로 작업한 것처럼 깔끔한 직선이 됩니다.
  • 병합 커밋이 없습니다.
병합(merge)리베이스(rebase)
이력 모양갈래가 보이는 그물망곧은 직선
병합 커밋생김안 생김
원본 커밋그대로 유지새로 만들어짐(해시 변경)
실제 역사사실대로 보존다시 쓰여짐
적합한 상황협업 메인 브랜치개인 브랜치 정리
💡 팁
정답은 없습니다. "있는 그대로의 역사"가 중요하면 merge, "읽기 좋은 깔끔한 역사"가 중요하면 rebase를 씁니다. 팀의 컨벤션을 따르세요.

9.3 리베이스 명령어와 실습

BASH
# feature 브랜치를 main 위로 다시 쌓기
git switch feature       # 옮길 브랜치로 이동
git rebase main          # main을 기준으로 재배치
CODE
Successfully rebased and updated refs/heads/feature.

이제 feature는 main의 최신 커밋 뒤에 자기 커밋들을 새로 쌓았습니다. 확인:

BASH
git log --oneline --graph

이후 main에서 feature를 병합하면 fast-forward로 깔끔하게 끝납니다.

BASH
git switch main
git merge feature        # 직선이므로 fast-forward
📌 핵심
기억: git rebase main은 "현재 브랜치를 main 위에 다시 쌓는다"는 뜻입니다. 그래서 옮기려는 브랜치(feature)로 먼저 이동한 뒤 실행합니다. (merge와 시작 위치가 반대라는 점에 주의!)

9.4 리베이스 충돌 해결

리베이스도 커밋을 하나씩 다시 적용하는 과정이라, 같은 부분을 건드렸다면 충돌이 납니다. 다만 해결 흐름이 merge와 조금 다릅니다.

BASH
git rebase main
CODE
CONFLICT (content): Merge conflict in app.js
error: could not apply a1b2c3d... Add feature

해결 절차:

BASH
# 1. 충돌 파일을 열어 <<< === >>> 를 정리 (merge와 동일)
# 2. 해결한 파일을 add
git add app.js

# 3. 병합처럼 commit 하지 않는다! 대신 계속 진행:
git rebase --continue

# (이 커밋을 건너뛰고 싶으면)
git rebase --skip

# (도저히 안 되겠으면 통째로 취소)
git rebase --abort
명령의미
git rebase --continue충돌 해결 후 다음 커밋으로 진행
git rebase --skip현재 커밋을 건너뛰기
git rebase --abort리베이스 전체 취소(원상 복귀)
⚠️ 흔한 실수
merge와의 차이: merge 충돌은 git commit으로 마무리하지만, rebase 충돌은 git rebase --continue로 이어갑니다. 여러 커밋을 재적용하다 보면 충돌이 여러 번 날 수도 있으니, 그때마다 해결 → --continue를 반복합니다.

막혔을 때 git rebase --abort는 언제나 안전한 탈출구입니다. 기억해두세요.

9.5 대화형 리베이스 — 커밋 수정·합치기

-i(interactive) 옵션을 쓰면 과거 커밋들을 정리할 수 있습니다. 지저분한 커밋 여러 개를 깔끔하게 다듬는 강력한 도구입니다.

BASH
# 최근 3개 커밋을 대화형으로 정리
git rebase -i HEAD~3

에디터가 열리고 다음과 같은 목록이 나옵니다.

CODE
pick a1b2c3d Add login form
pick b2c3d4e Fix typo
pick c3d4e5f Fix another typo

각 줄 앞의 pick을 다음 명령어로 바꿔 편집합니다.

명령의미
pick커밋 그대로 사용
reword커밋 메시지만 수정
edit커밋 내용 수정
squash이전 커밋과 합치기(메시지 합침)
fixup이전 커밋과 합치되 메시지 버림
drop커밋 삭제

예: 사소한 오타 수정 커밋 2개를 첫 커밋에 합치기

CODE
pick a1b2c3d Add login form
fixup b2c3d4e Fix typo
fixup c3d4e5f Fix another typo

저장하고 닫으면 3개 커밋이 1개로 깔끔하게 합쳐집니다. "Fix typo" 같은 잡음 커밋을 정리해 의미 있는 단위로 만들 수 있습니다.

9.6 리베이스의 황금률 ⚠️ (가장 중요!)

### 🚨 이미 공유한(push한) 커밋은 절대 리베이스하지 마라.

리베이스는 커밋을 새로 만듭니다(해시가 바뀝니다). 즉 원본 커밋을 버리고 다른 커밋으로 대체하는 것입니다.

내 컴퓨터에만 있는 커밋이라면 문제없습니다. 하지만 이미 원격에 push해서 동료가 그 커밋을 받아간 상태라면 재앙이 됩니다.

CODE
나:    (X) 커밋을 리베이스 → (X') 새 커밋으로 교체
동료:  여전히 옛날 (X) 커밋을 기준으로 작업 중
       ↓
       역사가 어긋나 충돌과 중복 커밋이 대량 발생! 😱

정리하면

상황리베이스 사용
내 로컬에만 있는 커밋✅ 마음껏 정리 OK
이미 push한 개인 브랜치 (혼자만 사용)⚠️ 가능하나 force push 필요, 주의
여러 명이 공유하는 브랜치(main 등)절대 금지
💡 팁
한 문장으로: "혼자 쓰는 커밋은 리베이스해도 되고, 남과 공유한 커밋은 병합하라."

부득이하게 push한 개인 브랜치를 리베이스했다면, 일반 push는 거부되고 강제 push가 필요합니다. 이때도 안전한 옵션을 쓰세요.

BASH
git push --force-with-lease     # 그냥 --force보다 안전 (남의 작업 덮어쓰기 방지)

9.7 이 장에서 배운 것 (요약)

  • 리베이스 = 브랜치의 출발점(base)을 옮겨 이력을 직선으로 정리
  • merge는 역사 보존(그물망), rebase는 역사 정리(직선)
  • git switch featuregit rebase main (옮길 브랜치에서 실행)
  • 충돌 시: 해결 → git addgit rebase --continue (commit 아님!)
  • 탈출구: git rebase --abort
  • git rebase -i로 커밋 합치기(squash/fixup)·메시지 수정(reword)
  • 황금률: 공유한(push한) 커밋은 리베이스 금지 — 어쩔 수 없으면 --force-with-lease

✍️ 확인 문제

  1. merge와 rebase의 이력 모양은 각각 어떻게 다른가요?
  2. 리베이스 충돌을 해결한 뒤 입력하는 명령은 (git commit이 아니라) 무엇인가요?
  3. 리베이스를 절대 하면 안 되는 상황은?
다음 장에서는 커밋을 되돌리는 두 가지 방법, 리셋(Reset)과 리버트(Revert)를 배웁니다. → 10_리셋과리버트.md