ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • rebase가 위험하다는 말의 진짜 의미 — 커밋은 이동하지 않고 복제된다
    IT 2026. 8. 6. 22:00
    rebase가 위험하다는 말의 진짜 의미 — 커밋은 이동하지 않고 복제된다

    git을 배우는 지인에게 rebase를 소개했더니 바로 이런 반응이 돌아왔다. "찾아보니까 다들 위험하다던데? 히스토리가 날아간다며?" 커뮤니티에는 실제로 "공개된 브랜치에서는 절대 rebase 하지 마라"가 격언처럼 돌고, Pro Git 책에도 "Do not rebase commits that exist outside your repository and that people may have based work on"이라고 박혀 있다. 그런데 "위험하다"만 듣고 이유를 모르면 두 가지 잘못된 극단으로 간다 — rebase를 아예 안 쓰거나(깔끔한 히스토리 도구를 버림), 규칙의 경계를 모른 채 쓰다가 사고를 낸다.

    이 글은 그 격언의 근거 하나를 파고든다. rebase는 커밋을 옮기는 게 아니라 복제한 뒤 원본을 버린다 — 위험의 전부가 이 한 문장에서 나온다.

    커밋 해시는 "내용의 지문"이다

    먼저 전제 하나. git의 모든 커밋은 40자리 해시(hash, 커밋의 내용으로 계산되는 고유 ID — a3f8c21... 같은 값)로 식별된다. 이 해시는 난수가 아니라 커밋의 구성 요소 전체를 재료로 계산한 지문이다: 변경된 파일 내용, 작성자, 시각, 커밋 메시지, 그리고 부모 커밋의 해시까지.

    마지막 재료가 결정적이다. 부모 해시가 재료에 들어가므로, 커밋의 내용이 한 글자도 안 바뀌어도 부모가 바뀌면 해시가 바뀐다. 그런데 rebase가 하는 일이 정확히 "커밋의 부모를 바꿔 다는 것"이다. 그래서 rebase를 통과한 커밋은 전부 새 해시를 받는다 — git 입장에서는 원본과 아무 관계가 없는 다른 커밋이다.

    diagram

    다이어그램 설명. "rebase 전" 커밋과 "rebase 후" 커밋은 코드 변경 내용이 완전히 같아도 부모가 다르므로 해시가 다르다. 그리고 "원본 F1은 브랜치에서 떨어져 나가" 단계가 흔히 놓치는 부분이다 — rebase는 원본을 지우는 게 아니라 브랜치 이름표를 복제본 쪽으로 옮겨서 원본이 이름 없는 미아가 되게 한다(미아 커밋은 한동안 git reflog로 복구 가능하다가, 유예 기간이 지나면 청소된다). "이동"처럼 보이는 것의 실체가 "복제 + 원본 유기"라는 것, 이게 위험을 이해하는 열쇠다.

    혼자일 땐 무해하다 — 사고는 "공유" 순간 시작된다

    내 로컬에만 있는 브랜치를 rebase하는 건 아무리 해도 안전하다. 복제되든 해시가 바뀌든, 그 커밋을 아는 사람이 나뿐이기 때문이다. 문제는 이미 push해서 동료가 받아 간 커밋을 rebase할 때 터진다. 시나리오로 보자.

    동료와 내가 같은 브랜치 feature를 공유 중이다. 커밋 "F1"까지 서로 같은 상태에서, 내가 로컬에서 git rebase main을 돌려 "F1"이 "F1'"로 복제됐다. 이걸 원격에 반영하려고 push하면 git이 거부한다 — 원격의 역사(F1)와 내 역사(F1')가 갈라져서, 일반 push로는 원격 커밋을 잃게 되기 때문이다. 여기서 많은 사람이 검색으로 찾은 해법을 그대로 붙여 넣는다: git push --force. 원격 브랜치가 내 역사로 덮어써진다.

    diagram

    다이어그램 설명. 사고의 전개다. "내가 로컬에서 rebase" 단계까지는 아무 문제가 없다 — 위험이 현실이 되는 건 "git push --force"로 다시 쓴 역사를 공유 공간에 강제로 올리는 순간이다. 그다음 "동료가 pull" 단계에서, 동료의 git은 자기가 가진 "F1"과 원격의 "F1'"를 남남으로 취급한다(해시가 다르니까). 내용이 같은 커밋 두 벌이 동시에 존재하는 상태에서 git은 이를 병합하려 들고, 동료는 자기가 만들지도 않은 충돌을 풀어야 한다. 심지어 동료가 "F1" 위에 자기 커밋을 쌓아 뒀다면 그 커밋들은 버려진 원본 위에 지어진 집이 된다. 격언이 말하는 "위험"의 실체는 rebase 자체가 아니라 rebase가 강요하는 force push, 그리고 그것이 동료의 로컬 역사와 일으키는 불일치다.

    경계선은 한 문장 — "그 커밋을 나만 아는가"

    이제 규칙을 정확한 경계로 다시 쓸 수 있다. "rebase 하지 마라"가 아니라:

    상황 rebase 이유
    로컬에만 있는 브랜치 자유롭게 OK 복제·해시 변경을 아는 사람이 나뿐
    push했지만 나만 쓰는 PR 브랜치 대체로 OK 덮어써도 피해자가 나뿐. 단 force push 필요
    동료와 공유 중인 브랜치 금지 동료 로컬의 역사와 불일치 발생
    main / 릴리스 브랜치 절대 금지 전원의 로컬·CI·배포가 그 역사에 기반

    표의 두 번째 행이 실무에서 제일 자주 만나는 회색지대인데, 자기 PR 브랜치를 rebase로 정돈한 뒤 올리는 것은 널리 쓰이는 워크플로우다. 이때 --force 대신 --force-with-lease를 쓰면 안전판이 하나 생긴다 — 원격이 내가 마지막으로 본 상태 그대로일 때만 덮어쓰고, 그 사이 누군가 push했으면 거부한다. 무조건 덮어쓰는 --force와 달리 "모르는 사이 남의 커밋을 지우는" 최악의 경우를 막아 준다.

    # 나쁜 습관: 원격 상태와 무관하게 무조건 덮어쓴다
    git push --force
    
    # 안전판: 원격이 내가 아는 상태일 때만 덮어쓴다.
    # 그 사이 동료가 push했다면 "stale info"로 거부된다
    git push --force-with-lease
    

    코드 설명. 두 명령의 차이는 "덮어쓰기 전에 원격을 확인하는가"다. --force-with-lease는 임대차 계약(lease)처럼 "내가 마지막으로 확인한 원격 상태"를 조건으로 걸어, 조건이 깨져 있으면 push를 거부한다. rebase를 쓰는 사람이라면 --force를 아예 손에서 지우고 이것만 쓰는 습관이 사고 확률을 크게 줄인다.

    덤 — 같은 원리가 적용되는 다른 명령들

    "이미 공유한 커밋의 해시를 바꾸는 행위는 전부 같은 위험"이라는 원리를 알면, rebase 외의 명령들도 같은 잣대로 판단할 수 있다. git commit --amend(마지막 커밋을 고쳐 쓰기 — 해시가 바뀐다), git rebase -i(interactive rebase, 커밋을 합치고 지우고 순서를 바꾸는 히스토리 편집기 — 손대는 모든 커밋의 해시가 바뀐다)도 push 전의 로컬 커밋에는 훌륭한 정돈 도구지만, push한 커밋에 쓰는 순간 force push가 강제되는 같은 길로 들어선다. 명령이 위험한 게 아니라 타이밍이 위험한 것이다.

    정리 — 위험은 rebase에 있지 않고 "공유된 역사 다시 쓰기"에 있다

    rebase는 커밋을 복제하고 원본을 버리는 명령이며, 그 결과 해시가 바뀐다. 나만 아는 커밋이면 무해하고, 남이 아는 커밋이면 그 사람의 로컬 역사와 어긋난다 — 위험의 전부가 이것이다. "공개 브랜치에서 rebase 금지"라는 격언은 이 원리의 축약본일 뿐이니, 격언 대신 원리를 기억하면 된다. 이 커밋을 아는 사람이 나뿐인가? 그렇다면 마음껏 다시 써라. 아니라면 역사는 이미 공공재다.


    참고한 공개 자료:

    • Pro Git Book — Rebasing (The Perils of Rebasing): https://git-scm.com/book/en/v2/Git-Branching-Rebasing
    • git-push 공식 문서 (--force-with-lease): https://git-scm.com/docs/git-push
    • Pro Git Book — Git Internals, Git Objects (커밋 해시 계산): https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
    • Atlassian Git Tutorial — Merging vs. Rebasing (Golden Rule of Rebasing): https://www.atlassian.com/git/tutorials/merging-vs-rebasing

    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.