팀 프로젝트를 진행하다 보면 브랜치의 커밋 히스토리가 복잡하게 얽혀서 어려움을 겪는 경우가 많습니다. 여러 갈래로 뻗어 나간 커밋 로그를 보며 "이걸 좀 더 깔끔하게 만들 순 없을까?" 하고 고민해 본 적이 있다면, 이 글이 도움이 되길 바랍니다. Git의 강력한 기능 중 하나인 rebase가 바로 그 고민을 해결해 줄 수 있는 열쇠입니다.
Git Rebase란 무엇인가?
git rebase는 한 브랜치에서 이루어진 커밋들을 다른 브랜치의 최신 커밋 위로 "재배치(re-base)"하는 명령어입니다. 쉽게 말해, 브랜치의 시작점(base)을 새로 지정하여 커밋 히스토리를 선형적(linear)으로 곧게 펴주는 역할을 합니다. 이 과정을 통해 프로젝트의 커밋 히스토리는 불필요한 병합(merge) 커밋 없이 매우 깔끔하게 정리됩니다.
Rebase의 주된 목적은 깨끗하고 선형적인 커밋 히스토리를 유지하는 것입니다. 이는 다른 개발자들이 프로젝트의 변경 사항을 더 쉽게 이해하고 추적할 수 있도록 돕습니다.
Rebase vs Merge: 무엇이 다를까?
`rebase`와 `merge`는 다른 브랜치의 변경사항을 통합하는 공통된 목표를 가지지만, 그 방식과 결과는 매우 다릅니다. 어떤 상황에 어떤 명령어를 사용해야 하는지 아는 것은 매우 중요합니다. 아래 표를 통해 두 명령어의 핵심적인 차이를 알아보겠습니다.
| 특징 | git rebase | git merge |
|---|---|---|
| 커밋 히스토리 | 선형적 (Linear): 깨끗하고 추적하기 쉬운 히스토리를 만듭니다. | 비선형적 (Non-linear): 실제 개발의 흐름을 보존하는 병합 커밋을 생성합니다. |
| 히스토리 변경 | 기존 커밋을 새로 작성하여 히스토리를 변경합니다. | 기존 히스토리를 변경하지 않고 그대로 유지합니다. |
| 충돌 해결 | 각 커밋을 적용할 때마다 충돌이 발생할 수 있어, 여러 번 해결해야 할 수 있습니다. | 병합 시점에 한 번만 충돌을 해결합니다. |
| 주요 사용 사례 | 로컬 브랜치의 커밋을 정리하거나, `main`의 최신 변경사항을 반영할 때 사용합니다. | 공유 브랜치의 작업을 통합하거나, 기능 개발 완료 후 `main`에 병합할 때 사용합니다. |
원격 저장소에 푸시(push)된 커밋, 즉 여러 개발자가 공유하는 브랜치에 `rebase`를 적용하고 `force push`하는 것은 매우 위험한 행동입니다. 이는 팀원의 작업 내용을 덮어쓸 수 있으며, 저장소의 커밋 히스토리를 심각하게 훼손할 수 있습니다.
만약 rebase된 브랜치를 푸시해야만 한다면, `git push --force` 대신 `git push --force-with-lease` 사용을 강력히 권장합니다. 이 옵션은 원격 브랜치가 마지막으로 확인한 상태와 동일할 때만 푸시를 허용하여, 다른 팀원이 그 사이에 푸시한 내용을 덮어쓰는 것을 방지해주는 더 안전한 대안입니다.
인터랙티브 Rebase로 커밋 다루기
`git rebase`의 진정한 강력함은 `-i` 또는 `--interactive` 옵션에서 나옵니다. 인터랙티브 rebase를 사용하면 커밋을 재배치하는 것 이상으로, 커밋을 수정, 결합, 삭제하는 등 히스토리를 자유롭게 편집할 수 있습니다.
인터랙티브 Rebase 시작하기
예를 들어, 최신 3개의 커밋을 수정하고 싶다면 다음 명령어를 사용합니다.
git rebase -i HEAD~3
이 명령을 실행하면 텍스트 편집기가 열리며, 수정할 커밋 목록과 함께 사용할 수 있는 명령어들이 나타납니다.
- pick: 커밋을 그대로 사용합니다.
- reword: 커밋 메시지를 수정합니다.
- edit: 커밋을 수정합니다 (코드 변경 등).
- squash: 이전 커밋과 하나로 합칩니다.
- fixup: `squash`와 동일하지만, 합쳐지는 커밋의 메시지는 버립니다.
- drop: 커밋을 삭제합니다.
자주 묻는 질문 ❓
- `git rebase --continue`: 충돌 해결을 완료하고 rebase를 계속 진행합니다.
- `git rebase --abort`: rebase 작업을 완전히 취소하고, rebase 시작 전의 상태로 돌아갑니다.
- `git rebase --skip`: 현재 충돌이 발생한 커밋의 변경사항을 무시하고 rebase를 계속 진행합니다. (주의해서 사용해야 합니다)
`git rebase`는 분명 강력한 도구이지만, 그만큼의 책임이 따릅니다. 이 글을 통해 `rebase`의 개념을 정확히 이해하고, `merge`와의 차이점을 명확히 인지하여 프로젝트의 히스토리를 더욱 전문적으로 관리할 수 있기를 바랍니다. 더 궁금한 점이 있다면 언제든지 질문해 주시기 바랍니다.