git rebase
Reapply commits on top of another base tip
— Git 공식 매뉴얼(man git-rebase)
결과물은 예술 그 자체처럼 일직선으로 아름답지만, 그 과정에서 치러야 할 충돌 해결의 대가는 너무나 참혹하다. 팀장님 죄송합니다, rebase --abort 치고 그냥 merge 갈기겠습니다(...)
1. 개요
갈라져 나간 내 브랜치의 뿌리(Base)를 타겟 브랜치의 최신 커밋 머리 위로 스르륵 뽑아서 다시 이식하는 기묘하고 강력한 기법이다. 무분별한 merge commit으로 인한 히스토리 더러움 현상을 원천 방지해 주어 깔끔함을 병적으로 숭상하는 고인물 집단이나 대기업 인프라 조직에서 표준으로 권장된다.
2. Merge vs Rebase: 성향의 갈림길
두 브랜치를 합치는 영원한 라이벌 관계다. git merge는 솔직하고 투박하게 '우리 여기서 만나서 합류했음!' 하고 커밋을 새로 하나 남겨 역사를 보존하지만, 수백 명이 작업하면 역사가 그물망처럼 얽혀 가독성이 멸망한다. 반면 git rebase는 내 커밋들을 가상 메모리에 임시로 빼두고, 목표 브랜치의 머리에 뿌리를 다시 내린 뒤 그 위에 커밋들을 한 땀 한 땀 차곡차곡 재배치한다. 덕분에 히스토리는 완벽한 일직선(Linear)을 유지하게 된다.1
3. 대악마: 연쇄 충돌 지옥(Conflict Loop)
만약 타겟 브랜치의 과거 코드 조각과 내 브랜치의 수십 개 커밋이 동일한 부위를 건드렸다면, Rebase 도중 끔찍한 사태가 발생한다. 커밋 하나를 적용할 때마다 매 단계마다 충돌(Conflict)이 터지는데, 충돌 해결 후 git rebase --continue를 치면 다음 커밋에서 또 충돌이 터지고, 이것이 무한 반복된다. 결국 길을 잃어 rebase --abort를 치고 원점으로 도망치게 만드는 심리적 장벽이 대단히 두텁다.
4. 실제 사용 예시
내 작업 브랜치를 최신 main 위로 다시 얹는 전형적인 흐름이다. 충돌이 나면 파일을 고친 뒤 계속 진행하거나, 포기하고 원래대로 돌아갈 수 있다.
git switch feature/login
git fetch origin
git rebase origin/main
# 충돌 해결 후
git add src/app.ts
git rebase --continue
# 너무 꼬였으면 중단
git rebase --abort
커밋 여러 개를 합치거나 메시지를 정리할 때는 인터랙티브 리베이스를 쓴다.
git rebase -i HEAD~3
5. 관련 밈 및 드립
5.1. rebase --force의 패기
공용 원격 브랜치에 있는 내 업로드를 로컬에서 멋대로 리베이스 세탁한 뒤, 서버에 그냥 올리면 당연히 뺀찌를 먹기 때문에 강제로 덮어씌워 버리는 git push --force 폭탄을 날려 다른 모든 팀원들의 로컬 환경을 순식간에 난지도 쓰레기장으로 파괴하는 빌런 밈이다. 발견 즉시 색출되어 조리돌림을 당하게 된다.
5.2. 스쿼시(Squash) 다이어트
rebase의 인터랙티브 모드(-i)를 켜고, 쓸모없이 잘게 쪼개져 있는 내 15개의 잡다구리한 '오타 수정', '이것도 수정' 커밋들을 단 하나의 고결하고 묵직한 커밋으로 압축하고 병합하는 기술이다. 겉보기엔 단번에 천재적으로 짠 코드처럼 미화할 수 있어 커밋 역사 조작 성형외과로 불린다.
6. 여담
- 골든 룰: '원격에 올라간 커밋은 리베이스하지 않는다'는 황금률만 지켜도 당신의 개발 수명은 비약적으로 연장된다.
- 체리 픽: rebase가 통째로 이식하는 거라면, 오직 마음에 드는 딱 한 송이의 커밋 체리만 떼어내어 내 브랜치에 가볍게 붙이는 git cherry-pick이라는 배다른 변종 명수도 존재한다.
- 자동 스태싱: rebase 도중 내 더러운 수정본 때문에 에러가 난다면, 뒤에
--autostash옵션을 발라두면 알아서 보따리에 내 물건을 숨겼다가 작업이 끝나면 돌려주므로 매우 영리하다.