변경사항을 통합하는 두 가지 방법
Git branch로 작업할 때, 한 branch의 변경사항을 다른 branch로 가져와야 하는 시점이 옵니다. Git은 이를 위한 두 가지 주요 전략을 제공합니다: merge와 rebase. 둘 다 동일한 최종 결과를 달성합니다 -- branch에 모든 최신 변경사항이 포함됩니다 -- 하지만 근본적으로 다른 방식으로 수행하며, 올바른 것을 선택하는 것이 프로젝트의 이력과 팀의 workflow에 중요합니다.
Merge의 동작 방식
merge는 두 branch를 가져와 새로운 merge commit을 생성하여 결합합니다. 이 merge commit에는 두 개의 부모가 있습니다: 현재 branch의 끝과 통합하려는 branch의 끝입니다.
git checkout feature/login
git merge main
이후, 기능 branch에는 merge commit으로 연결된 main의 모든 변경사항이 포함됩니다. 두 branch의 이력은 실제 발생한 그대로 정확히 보존됩니다. commit 그래프를 보면 두 개발 라인이 merge 지점에서 수렴하는 것을 볼 수 있습니다.
merge의 주요 특성:
- 비파괴적. 기존 commit이 변경되지 않습니다. 이력은 실제로 일어난 것 그대로입니다.
- Merge commit을 생성. 이 추가 commit이 두 이력을 하나로 묶습니다.
- 맥락을 보존. branch가 언제 생성되고 언제 다시 merge되었는지 항상 확인할 수 있습니다.
Fast-forward merge
branch가 대상에서 분기하지 않은 경우(branch를 생성한 이후 대상에 새로운 commit이 없는 경우), Git은 기본적으로 fast-forward merge를 수행합니다. 단순히 branch 포인터를 앞으로 이동시킵니다. 필요하지 않으므로 merge commit이 생성되지 않습니다. 이 경우에도 merge commit을 강제할 수 있습니다:
git merge --no-ff feature/login
merge가 사소한 것이더라도 작업이 별도의 branch에서 이루어졌다는 사실을 보존하고 싶을 때 유용합니다.
Rebase의 동작 방식
rebase는 다른 접근법을 취합니다. merge commit을 생성하는 대신, 다른 branch 위에 commit을 재적용합니다. 결과는 대상 branch의 최신 시점에서 작업을 시작한 것처럼 보이는 선형 이력입니다.
git checkout feature/login
git rebase main
내부에서 일어나는 일:
- Git이 두 branch의 공통 조상을 찾습니다.
- branch의 commit을 임시로 제거합니다.
- branch 포인터를 main의 끝으로 이동합니다.
- commit을 하나씩 그 위에 재적용합니다.
결과 이력은 완벽하게 선형입니다. merge commit 없이, 그래프에서 분기하고 수렴하는 선 없이. main의 최신 변경사항 이후에 모든 코드를 작성한 것처럼 보입니다. 실제로는 그렇지 않더라도.
rebase의 주요 특성:
- 선형 이력을 생성. commit 그래프가 깔끔하고 읽기 쉽습니다.
- Commit 해시를 재작성. 재적용된 commit은 내용이 동일하더라도 새로운 SHA 해시를 받습니다.
- Merge commit 없음. branch가 추가 commit 없이 깔끔하게 통합됩니다.
Merge를 사용해야 할 때
merge는 여러 상황에서 더 안전하고 간단한 선택입니다:
- 공유 branch. 여러 사람이 같은 branch에서 작업할 때, merge는 다른 사람이 의존하는 commit을 재작성하지 않고 모든 사람의 이력을 보존합니다.
- 이력 보존이 중요할 때. 변경사항이 정확히 언제 어떻게 통합되었는지 추적해야 하는 프로젝트에서, merge는 완전하고 변경되지 않은 기록을 제공합니다.
- 장기 branch 통합. 릴리스 branch를 main에 merge하거나, 크게 분기한 두 branch를 결합할 때, merge commit이 통합 지점을 명확하게 표시합니다.
- 단순함을 원할 때. merge는 개념적으로 더 쉽습니다. 특히 Git을 처음 접하는 개발자에게 문제가 발생할 가능성이 적습니다.
Rebase를 사용해야 할 때
rebase는 깔끔하고 선형적인 프로젝트 이력을 원할 때 빛납니다:
- 기능 branch 업데이트. 기능을 main에 merge하기 전에, 최신 main 위에 rebase하면 merge commit 없이 직접 적용할 수 있는 깔끔한 commit 세트를 얻을 수 있습니다.
- 깔끔한 이력 유지. 선형 이력은
git log로 탐색하기 쉽고, 버그를 추적할 때 bisect하기 쉽고, pull request에서 리뷰하기 쉽습니다. - 로컬 정리. 작업하면서 작은 증분 commit을 만들었다면, branch를 공유하기 전에 rebase를 사용하여 정리할 수 있습니다.
Rebase의 황금률
공유 branch에 push된 commit은 절대 rebase하지 마세요.
이것은 기억해야 할 가장 중요한 규칙입니다. rebase는 commit 해시를 재작성하므로, 원래 commit을 기반으로 작업한 다른 사람들에게 문제가 발생합니다. 그들의 이력이 rebase된 이력과 더 이상 일치하지 않아 commit 중복, 충돌, 혼란이 발생합니다.
규칙은 간단합니다: 다른 사람이 여러분의 commit을 pull했을 수 있다면, rebase하지 마세요. 자신의 로컬 branch에서만, 또는 자신이 유일한 기여자인 branch에서만 rebase를 사용하세요.
실수로 공유 branch를 rebase한 경우, 팀의 다른 개발자들이 분기된 이력을 처리해야 합니다. 이는 일반적으로 force-push(원격을 덮어씀)와 다른 모든 사람이 로컬 복사본을 reset하는 것을 의미합니다. 혼란을 초래하며 피할 수 있는 상황입니다.
인터랙티브 Rebase: 정밀한 이력 재작성
인터랙티브 rebase(git rebase -i)는 단순한 commit 이동을 넘어섭니다. commit 이력 자체를 수정할 수 있습니다:
git rebase -i HEAD~5
최근 5개 commit의 목록이 열리며, 다음을 할 수 있습니다:
- pick -- commit을 그대로 유지
- reword -- commit 메시지 변경
- edit -- commit을 수정하기 위해 일시 정지
- squash -- 이전 commit과 결합
- fixup -- squash와 같지만 commit 메시지 폐기
- drop -- commit을 완전히 제거
- reorder -- 줄을 재배열하여 순서 변경
인터랙티브 rebase는 merge 전에 지저분한 branch를 정리하는 데 강력합니다. "오타 수정" commit을 부모에 squash하고, 불명확한 메시지를 다시 작성하고, commit을 논리적인 스토리가 되도록 재배열할 수 있습니다.
실용적인 워크플로우
많은 팀이 두 전략의 장점을 결합한 접근법을 사용합니다:
- main에서 기능 branch를 생성합니다.
- 작업하면서 기능 branch를 main 위에 주기적으로 rebase하여 최신 상태를 유지합니다.
- Pull request를 열기 전에 인터랙티브 rebase를 사용하여 commit을 정리합니다.
- 기능 branch를 main에 merge합니다 (종종 통합 지점을 표시하기 위해 merge commit을 사용).
이를 통해 기능 branch에서는 깔끔하고 선형적인 이력을, main에서는 명확한 merge 지점을 얻을 수 있습니다. 대부분의 팀에 잘 맞는 실용적인 절충안입니다.
GitSquid에서의 Rebase와 Merge
GitSquid는 인터페이스에서 직접 merge와 rebase를 모두 지원합니다. commit 그래프의 branch 레이블에서 컨텍스트 메뉴를 통해 branch를 merge할 수 있습니다. rebase의 경우, GitSquid에는 터미널에서 텍스트 파일을 편집하는 대신 드래그 앤 클릭으로 commit을 재배열, squash, fixup, reword, drop할 수 있는 비주얼 인터랙티브 rebase 에디터가 포함되어 있습니다. 이를 통해 커맨드라인 버전이 어렵게 느껴지더라도 인터랙티브 rebase를 쉽게 사용할 수 있습니다.
요약
| Merge | Rebase | |
|---|---|---|
| 이력 | 비선형, branch 보존 | 선형, 깔끔 |
| Commit 해시 | 변경 없음 | 재작성됨 |
| Merge commit | 예 (fast-forward 제외) | 아니오 |
| 공유 branch에서 안전 | 예 | 아니오 |
| 최적 용도 | 공유 작업 통합 | 기능 branch 정리 |
merge도 rebase도 보편적으로 더 나은 것은 없습니다. 서로 다른 상황을 위한 도구입니다. 각각이 이력에 어떤 영향을 미치는지 이해하고, rebase의 황금률을 따르며, 상황에 맞는 것을 선택하세요. 미래의 자신이 git log를 읽을 때 감사할 것입니다.