git rebase vs merge — 히스토리 날아간다는데 진짜? 1분 완벽 정리 (2026)

“rebase 쓰면 커밋 히스토리가 날아간다” — 개발자라면 한 번쯤 들어본 말입니다. 절반은 맞고 절반은 틀립니다. 정확히 언제 merge를 쓰고 언제 rebase를 써야 하는지, 그리고 절대 rebase하면 안 되는 상황이 무엇인지 정리하겠습니다.


merge와 rebase의 본질적 차이

git merge와 git rebase는 둘 다 브랜치를 합치는 명령어지만, 작동 방식이 완전히 다릅니다. 한 줄로 요약하면 이렇습니다.

merge는 두 브랜치가 만났다는 사실을 기록으로 남깁니다. 병합 커밋(merge commit)이 생성되고, 히스토리에 “이 시점에 이 두 브랜치가 합쳐졌다”는 정보가 그대로 보존됩니다. 히스토리가 꼬이지 않고, 누가 언제 무엇을 했는지 그대로 추적할 수 있습니다.

rebase는 내 커밋들을 최신 main 위로 올려서 한 줄로 펴줍니다. 병합 커밋이 생성되지 않고, 커밋들이 마치 처음부터 main 위에서 작성된 것처럼 재배치됩니다. 히스토리가 깔끔해지지만, 원래 커밋의 순서와 위치 정보가 변경됩니다. Git 공식 문서(Git Book 한국어판)에서도 이 차이를 명확하게 설명하고 있으니 참고하세요.

merge: 히스토리를 그대로 보존한다

merge를 사용하는 기본 시나리오를 봅시다. feature 브랜치에서 작업을 완료한 뒤 main에 병합할 때:

# main 브랜치로 이동
git checkout main

# feature 브랜치 병합
git merge feature

# 병합 커밋이 생성됨
# 히스토리에 "Merge branch 'feature'" 커밋이 남음

이렇게 하면 main 브랜치에 병합 커밋이 하나 추가됩니다. 히스토리를 그래프로 보면 feature 브랜치가 갈라져 나갔다가 다시 main으로 합쳐지는 모양이 됩니다. 이 모양이 보기엔 지저분할 수 있지만, “언제 어떤 작업이 독립적으로 진행되었는지”를 정확하게 보여줍니다.

merge의 장점은 안전하다는 것입니다. 기존 커밋을 수정하지 않으니까 협업 중인 브랜치에서도 문제가 없습니다. 단점은 히스토리가 복잡해질 수 있다는 것입니다 — 작은 기능 하나 추가할 때마다 병합 커밋이 쌓이면, 나중에 git log --graph를 봤을 때 엉켜 있는 선들이 눈에 들어옵니다.

merge를 사용할 때 충돌이 발생하면, 해결하고 git add한 뒤 git commit으로 병합 커밋을 완성하면 됩니다. 충돌 해결 과정 자체가 하나의 커밋으로 기록되므로, 나중에 “이 병합에서 무엇을 어떻게 해결했는지”를 추적할 수 있습니다. 이것은 규제가 엄격한 환경이나 감사 추적이 필요한 프로젝트에서 큰 장점이 됩니다.

rebase: 한 줄로 깔끔하게 정리한다

rebase를 사용하는 시나리오입니다. feature 브랜치에서 작업하는 도중 main이 업데이트됐을 때:

# feature 브랜치에서
git checkout feature

# main의 최신 상태 위로 내 커밋을 올림
git rebase main

# 내 커밋들이 main 최신 커밋 뒤로 재배치됨
# 병합 커밋 없이 한 줄 히스토리가 됨

rebase를 하면 내 커밋들의 커밋 해시가 바뀝니다. 왜냐하면 커밋의 “부모”가 변경되기 때문입니다. 기존 커밋 A → B → C가 main의 최신 커밋 M 뒤로 재배치되어 M → A’ → B’ → C’가 됩니다. 내용은 같지만 해시가 다른 새로운 커밋이 만들어지는 것입니다.

바로 이것이 “히스토리가 날아간다”는 말의 진짜 의미입니다. 원본 커밋 A, B, C는 사라지고 새로운 A’, B’, C’로 대체됩니다. 로컬에서만 쓰는 브랜치라면 아무 문제가 없지만, 다른 사람과 공유하는 브랜치라면 큰 문제가 됩니다.

충돌 해결: rebase 도중 충돌이 나면

rebase를 실행했을 때 충돌이 발생하면, git은 rebase를 일시 중지하고 충돌을 알려줍니다. merge와 달리 rebase는 커밋을 하나씩 올리기 때문에, 충돌이 발생한 커밋만 해결하면 됩니다. 충돌 파일을 열어서 수정한 뒤 git add로 스테이징하고, git rebase --continue로 다음 커밋으로 넘어갑니다.

커밋이 5개 있고 3번째 커밋에서 충돌이 났다면, 3번째만 해결하고 continue하면 4번째부터 계속 진행됩니다. 만약 충돌이 너무 복잡해서 처음부터 다시 하고 싶다면 git rebase --abort를 입력하면 rebase 시작 전 상태로 돌아갑니다. rebase를 취소해도 원본 커밋은 그대로 남아있으니 안전합니다.

충돌이 같은 부분에서 반복적으로 발생한다면, git rerere(Reuse Recorded Resolution) 기능을 활성화해 두면 이전에 해결한 충돌 패턴을 git이 기억해서 자동으로 적용해 줍니다. git config --global rerere.enabled true 한 줄로 켤 수 있습니다. rebase를 자주 쓰는 팀이라면 유용한 기능입니다.

절대 하면 안 되는 것: 공유 브랜치에서 rebase

rebase의 황금률은 단 하나입니다. 이미 push한 커밋은 rebase하지 마라.

이유를 시나리오로 설명하겠습니다. 팀원 A와 B가 같은 feature 브랜치에서 작업하고 있다고 가정합니다. A가 커밋 3개를 push했습니다. B가 pull받아서 그 위에 작업을 이어가고 있습니다.

이때 A가 “히스토리가 지저분하네” 하고 rebase를 한 다음 force push를 했습니다. A의 로컬에서는 커밋 해시가 A1 → A2 → A3에서 A1′ → A2′ → A3’로 바뀝니다.

B가 다시 pull하려고 하면? B의 로컬에는 원래 A1, A2, A3가 있고, 원격에는 A1′, A2′, A3’이 있습니다. git은 이걸 완전히 다른 커밋으로 인식합니다. B가 pull하면 이 두 히스토리가 합쳐지면서 커밋이 중복되고, 히스토리가 엉키고, 충돌이 반복해서 발생합니다. 팀원 전체가 고통받게 됩니다.

그래서 rebase는 내 로컬 브랜치에서만 써야 합니다. push하기 전에 로컬에서 정리할 때만 rebase를 쓰고, 한 번 push한 커밋은 그대로 둡니다.

실전: PR 올리기 전 rebase로 깔끔하게

가장 흔한 rebase 사용 패턴은 PR(Pull Request)을 올리기 전에 커밋을 정리하는 것입니다. feature 브랜치에서 작업하다 보면 “오타 수정”, “lint 수정”, “다시 오타 수정” 같은 잡음 커밋이 쌓입니다. 이걸 그대로 PR에 올리면 리뷰어가 읽기 힘듭니다.

# 최신 main을 가져오고
git fetch origin
git rebase origin/main

# 커밋 정리 (interactive rebase)
git rebase -i HEAD~5
# squash로 잡음 커밋을 하나로 합치기

이렇게 하면 최신 main 위에 정리된 커밋만 남습니다. PR을 올리면 리뷰어가 깔끔한 커밋 히스토리를 볼 수 있습니다. 아직 push하지 않았으니 아무에게도 영향을 주지 않습니다.

interactive rebase(-i 옵션)에서 사용할 수 있는 명령은 squash 외에도 여러 가지가 있습니다. reword는 커밋 메시지만 수정하고 내용은 그대로 둡니다. edit는 해당 커밋에서 잠시 멈춰서 코드를 수정한 뒤 계속 진행합니다. drop은 커밋을 아예 삭제합니다. 이 명령들을 조합하면 커밋 히스토리를 원하는 형태로 자유롭게 재구성할 수 있습니다.

실수했을 때 복구하기: git reflog

rebase를 잘못했을 때 당황하지 마세요. git은 모든 HEAD 이동을 기록합니다. git reflog를 실행하면 커밋 해시 변경 이력이 시간순으로 나옵니다. rebase하기 전의 HEAD 위치를 찾아서 git reset --hard HEAD@{N}으로 되돌리면 rebase 이전 상태로 완전히 복구됩니다.

reflog는 로컬 기록이므로 원격에 push한 내용에는 영향을 주지 않습니다. 또한 reflog 기록은 기본적으로 90일간 보관되므로, 당장 실수를 발견하지 못해도 나중에 복구할 수 있는 창이 넓습니다. rebase로 히스토리가 꼬였을 때 가장 먼저 확인해야 하는 것이 reflog입니다.

세 번째 선택지: squash merge

merge와 rebase 외에 최근 많이 쓰이는 방식이 squash merge입니다. feature 브랜치의 모든 커밋을 하나로 합쳐서 main에 새 커밋 하나로 추가하는 방식입니다. GitHub에서 “Squash and merge” 버튼을 누르면 이 방식이 적용됩니다. feature 브랜치에서 커밋이 10개였어도 main에는 하나의 커밋만 남습니다.

squash merge의 장점은 main 히스토리가 매우 깔끔해진다는 것입니다. 기능 하나당 커밋 하나이므로, git log --oneline을 봤을 때 각 커밋이 하나의 완성된 기능을 의미합니다. rebase처럼 히스토리를 재작성하는 위험도 없고, merge처럼 병합 커밋으로 히스토리가 지저분해지지도 않습니다. 다만 개별 작업 단계(디버깅, 시도착오 과정)는 사라지므로, 상세한 작업 이력이 필요한 팀에서는 단점이 될 수 있습니다.

팀 규칙을 기준으로 선택하라

merge와 rebase 중 뭐가 “맞다”는 정답은 없습니다. 팀의 규칙에 따라 선택하면 됩니다.

merge를 기본으로 쓰는 팀은 히스토리의 정확성을 우선시합니다. “언제 어떤 작업이 병합되었는지”를 보존하는 것이 더 중요하다고 판단합니다. 대규모 프로젝트, 규제가 있는 환경, 감사 추적이 필요한 경우에 적합합니다.

rebase를 기본으로 쓰는 팀은 히스토리의 가독성을 우선시합니다. 한 줄로 된 깔끔한 히스토리가 코드 리뷰와 버그 추적에 더 도움이 된다고 판단합니다. 소규모 팀, 빠르게 움직이는 스타트업, 오픈소스 프로젝트에서 흔히 채택합니다.

중요한 것은 팀 전체가 같은 규칙을 따르는 것입니다. 어떤 팀원은 merge만 하고 다른 팀원은 rebase를 쓰면 히스토리가 혼란스러워집니다. 다음 PR 전에 팀의 rebase/merge 규칙을 한번 확인해 보세요.

실제 팀 워크플로우: GitHub Flow와 rebase

실제 많은 팀이 사용하는 워크플로우를 구체적으로 살펴보겠습니다. GitHub Flow를 기반으로 rebase를 활용하는 패턴입니다. 이 패턴은 소규모 스타트업부터 대규모 오픈소스 프로젝트까지 널리 쓰이는 방식입니다.

먼저 main 브랜치에서 feature 브랜치를 만듭니다. 작업하면서 수시로 커밋을 쌓습니다. 이때 다른 팀원이 main에 새로운 코드를 merge했다면, 내 feature 브랜치가 최신 main보다 뒤처지게 됩니다. 여기서 선택지가 두 가지입니다.

첫 번째는 git merge main으로 main을 내 브랜치에 병합하는 것입니다. 병합 커밋이 생기지만 기존 커밋이 수정되지 않으므로 안전합니다. 두 번째는 git rebase main으로 내 브랜치를 최신 main 위로 올리는 것입니다. 히스토리가 한 줄로 깔끔해지지만 커밋 해시가 바뀝니다. 아직 push하지 않은 로컬 브랜치라면 rebase가 좋은 선택입니다.

PR을 올리기 전에 마지막으로 interactive rebase로 커밋을 정리합니다. “오타 수정”, “lint 적용”, “다시 오타 수정” 같은 잡음 커밋을 squash로 합치고, 커밋 메시지를 명확하게 다시 작성합니다. 이렇게 정리된 PR은 리뷰어가 읽기 쉽고, 나중에 main에 merge된 후에도 git log가 깔끔하게 유지됩니다. 리뷰어가 “이 커밋에서 실제로 바뀐 게 뭔가요?”라고 물어보지 않아도 됩니다.

한 가지 주의할 점은 PR을 올린 후에 커밋을 추가로 정리하면 force push가 필요하다는 것입니다. 이때는 일반 --force 대신 --force-with-lease를 사용해야 합니다. --force-with-lease는 다른 사람이 같은 브랜치에 push한 변경사항을 실수로 덮어쓰는 것을 방지합니다. feature 브랜치는 보통 한 명만 작업하므로 안전하지만, 습관적으로 --force-with-lease를 쓰는 것이 바람직합니다.

rebase 도중 포기하기: abort와 reset

rebase를 하다 보면 충돌이 너무 복잡해서 처음부터 다시 하고 싶은 순간이 옵니다. 이때 두 가지 방법이 있습니다. 첫 번째는 git rebase --abort입니다. rebase를 즉시 취소하고 rebase 시작 전 상태로 돌아갑니다. 충돌 해결 중간에 포기할 때 가장 안전하고 빠른 방법입니다.

두 번째는 git reflog로 rebase 전 HEAD 위치를 찾아 git reset --hard HEAD@{N}로 되돌리는 것입니다. abort와 결과는 같지만, rebase가 이미 완료된 후에도 사용할 수 있다는 점이 다릅니다. rebase를 완료했는데 결과가 마음에 들지 않으면 reflog로 언제든 되돌릴 수 있습니다.

중요한 것은 rebase는 로컬 작업이라는 점입니다. 아직 push하지 않았다면 언제든 안전하게 되돌릴 수 있습니다. 당황하지 말고 git reflog를 먼저 확인하는 습관을 들이면 어떤 git 실수든 복구할 수 있습니다. reflog는 로컬에만 기록되므로 원격에 영향을 주지 않으며 기본 90일간 보관됩니다. 3개월 안에 발견하면 복구 가능합니다.

충돌이 같은 부분에서 반복적으로 발생한다면 git rerere 기능을 활성화해 보세요. git config --global rerere.enabled true 한 줄로 켤 수 있으며, 이전에 해결한 충돌 패턴을 git이 기억해서 다음에 같은 충돌이 나면 자동으로 동일하게 해결합니다. rebase를 자주 쓰는 팀이라면 특히 유용한 기능입니다.

요약 비교표

항목 merge rebase
히스토리 보존 (병합 커밋 생성) 재작성 (한 줄 정리)
안전성 공유 브랜치에서 안전 공유 브랜치에서 위험
커밋 해시 변경 없음 변경됨
적용 시점 main에 병합할 때 PR 전 로컬 정리
충돌 처리 병합 시 1회 커밋마다 발생 가능

핵심만 기억하세요. merge는 히스토리를 보존하고, rebase는 한 줄로 정리한다. 공유 브랜치에서는 절대 rebase하지 않는다. 팀 규칙을 기준으로 선택한다. 이 세 가지만 지키면 됩니다. VPS에서 git 작업을 할 때는 tmux 세션을 활용하면 SSH 연결이 끊겨도 안전하게 rebase를 마칠 수 있습니다.

관련 글