git rebase 사용법과 주의사항
Git으로 협업하다 보면 커밋 이력이 점점 복잡해지는 경험을 한 번쯤 해보셨을 텐데요.
git rebase는 커밋 이력을 깔끔하게 재정리해주는 명령어입니다.
git merge와 비슷하게 브랜치를 합치는 역할을 하지만, 이력을 다루는 방식에서 큰 차이가 있습니다.
merge vs. rebase
merge와 rebase의 차이를 그림으로 살펴보겠습니다.
main에서 feature 브랜치를 만든 뒤, 양쪽 모두 새 커밋이 생긴 상황입니다.
main: A --- B --- E
\
feature: C --- D
여기서 merge를 하면 두 브랜치를 합치는 병합 커밋(M)이 생깁니다.
main: A --- B --- E --- M
\ /
feature: C --- D
반면 rebase를 하면 feature의 커밋들(C, D)을 main의 최신 커밋(E) 뒤로 옮겨 놓습니다.
main: A --- B --- E
\
feature: C' --- D'
원래의 C, D 커밋이 E 이후로 다시 적용되면서 C’, D’이라는 새로운 커밋이 만들어집니다. 커밋의 내용은 같지만 커밋 해시(ID)는 달라지는 점에 유의하세요.
결과적으로 feature 브랜치의 이력이 마치 main의 최신 상태에서 작업을 시작한 것처럼 깔끔한 일직선이 됩니다.
기본 사용법
feature 브랜치에서 작업 중이고, main 브랜치의 최신 변경분을 가져오고 싶다면 다음과 같이 실행합니다.
$ git switch feature/login
$ git rebase main
Successfully rebased and updated refs/heads/feature/login.
이 명령어는 feature/login 브랜치의 커밋들을 main 브랜치의 최신 커밋 위에 하나씩 다시 적용합니다.
rebase가 끝난 뒤에 main에서 feature/login을 merge하면 fast-forward merge가 되어 이력이 깔끔하게 유지됩니다.
$ git switch main
$ git merge feature/login
실전 워크플로우
팀에서 rebase를 활용하는 가장 흔한 시나리오를 소개하겠습니다.
기능 브랜치에서 작업하는 동안 main에 다른 사람의 커밋이 추가된 상황입니다.
Pull Request를 보내기 전에 main의 최신 내용을 반영하고 싶은데요.
# main 브랜치의 최신 내용을 가져온다
$ git fetch origin
# 내 feature 브랜치를 origin/main 위에 rebase한다
$ git rebase origin/main
이렇게 하면 PR에서 충돌 없이 깔끔하게 merge할 수 있습니다.
다만, 이미 원격에 push한 브랜치를 rebase하면 커밋 해시가 바뀌기 때문에 강제 push가 필요합니다.
$ git push -f origin feature/login
혼자 사용하는 기능 브랜치라면 강제 push를 해도 괜찮지만, 여러 사람이 같은 브랜치에서 작업하고 있다면 절대 하면 안 됩니다.
충돌 해결
rebase 중에도 충돌이 발생할 수 있습니다.
커밋을 하나씩 다시 적용하는 과정에서 main의 변경 내용과 충돌하는 부분이 있으면 Git이 멈추고 알려줍니다.
$ git rebase main
CONFLICT (content): Merge conflict in src/app.js
error: could not apply c1a2b3c... 사용자 인증 로직 추가
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add <pathspec>" then run "git rebase --continue".
충돌을 해결하는 순서는 다음과 같습니다.
# 1. 충돌 파일을 열어서 수정한다
# 2. 수정한 파일을 스테이징한다
$ git add src/app.js
# 3. rebase를 계속 진행한다
$ git rebase --continue
커밋이 여러 개라면 각 커밋마다 충돌이 발생할 수 있고, 그때마다 위 과정을 반복해야 합니다.
충돌이 너무 복잡해서 rebase 자체를 취소하고 싶다면 이렇게 합니다.
$ git rebase --abort
rebase 시작 전 상태로 깔끔하게 되돌아갑니다.
rebase 주의사항
rebase를 사용할 때 가장 중요한 원칙이 하나 있습니다.
다른 사람과 공유하는 브랜치의 커밋은 rebase하지 않는다.
rebase는 기존 커밋을 새로운 커밋으로 다시 만드는 작업이기 때문에, 이미 원격에 올리고 다른 사람이 내려받은 커밋을 rebase하면 이력이 꼬여서 팀 전체가 곤란해질 수 있습니다.
안전한 사용 기준을 정리하면, 아직 push하지 않은 로컬 커밋은 자유롭게 rebase해도 되고, 혼자 사용하는 기능 브랜치에서 push한 커밋은 강제 push를 감수하면 가능합니다.
main이나 develop 같은 공유 브랜치의 커밋은 rebase하면 안 됩니다.
git pull —rebase
git pull에 --rebase 옵션을 붙이면 fetch 후에 merge 대신 rebase를 수행합니다.
$ git pull --rebase origin main
이 방식은 불필요한 병합 커밋을 만들지 않아서 이력을 깔끔하게 유지하고 싶을 때 유용합니다.
매번 옵션을 붙이기 번거롭다면 git config로 기본 동작을 설정할 수도 있습니다.
$ git config --global pull.rebase true
마치며
git rebase는 커밋 이력을 깔끔하게 관리하기 위한 강력한 도구입니다.
merge가 이력을 있는 그대로 보존하는 방식이라면, rebase는 이력을 재구성해서 읽기 쉽게 만드는 방식입니다.
둘 중 어떤 것이 “더 좋다”라고 할 수 없고, 팀의 Git 워크플로우에 맞는 방식을 선택하는 게 중요합니다. 공유 브랜치에서는 merge, 개인 브랜치에서는 rebase라는 기본 원칙만 지키면 대부분의 상황에서 문제없이 사용할 수 있을 것입니다.
더 자세한 내용은 Git 공식 문서를 참고하세요.
This work is licensed under
CC BY 4.0