Published on

Git rebase -i 대참사? PR 안전 복구 패턴

Authors

서론: git rebase -i는 커밋을 깔끔하게 만들 수 있는 강력한 도구지만, PR이 이미 올라간 상태에서 히스토리를 바꾸면 “내 PR이 사라졌어요”, “커밋이 중복돼요”, “리뷰 코멘트가 엉켜요” 같은 대참사가 자주 발생합니다. 문제의 본질은 단순합니다. 리베이스는 기존 커밋을 수정하는 게 아니라 새로운 커밋을 다시 쓰는 작업이고, 그 순간 기존 커밋 SHA가 바뀌면서 원격과 로컬의 연결이 끊기기 때문입니다.

이 글은 “리베이스를 하지 말자”가 아니라, PR이 있는 상황에서도 안전하게 리베이스하고, 사고가 나도 되돌릴 수 있는 복구 패턴을 정리합니다. 특히 팀에서 자주 쓰는 GitHub PR 흐름(브랜치 기반 협업)에서 바로 적용할 수 있도록, 명령어 중심으로 단계별 체크리스트를 제공합니다.

참고: CI/CD 파이프라인에서 브랜치 히스토리 변경이 배포에 어떤 영향을 주는지도 같이 고민해야 합니다. GitHub Actions 기반 배포 흐름을 운영 중이라면 GitHub Actions로 EKS 무중단 배포 - Blue-Green CI/CD 글도 함께 보면 “어떤 브랜치가 배포 트리거인가” 관점에서 사고 예방에 도움이 됩니다.

왜 PR에서 rebase -i가 위험해지는가

1) 커밋 SHA가 바뀌면 Git은 “다른 커밋”으로 본다

리베이스는 기존 커밋을 재배치하거나 합치면서 새 커밋을 생성합니다. 결과적으로 커밋 SHA가 바뀌고, 원격 브랜치에 이미 푸시된 커밋과 로컬 커밋의 관계가 달라집니다.

2) PR은 브랜치 포인터를 따라가지만, 리뷰 맥락은 커밋에 묶인다

GitHub PR은 브랜치의 최신 상태를 보여주지만, 리뷰 코멘트/스레드/라인 코멘트는 특정 커밋과 diff 맥락에 연결됩니다. 리베이스로 커밋이 갈아엎어지면 “Outdated”가 늘어나고, 리뷰어 입장에서는 변경 추적이 어려워집니다.

3) git push --force는 협업 브랜치에 치명적일 수 있다

강제 푸시는 원격 브랜치 포인터를 강제로 이동시키며, 다른 사람이 그 브랜치를 기반으로 작업하고 있으면 그 사람의 히스토리를 깨뜨릴 수 있습니다.

그래서 핵심은 이 한 문장입니다.

  • PR 브랜치에서 리라이트가 필요하면, 되돌릴 수 있는 안전장치충돌을 최소화하는 푸시 전략을 함께 써야 한다.

사고 예방: 리베이스 전에 반드시 하는 3가지

1) 원격 최신 상태 확인

git fetch origin

git status
# 브랜치가 ahead/behind 인지 확인

git log --oneline --decorate --graph --max-count=20

2) 백업 브랜치 만들기(로컬과 원격 둘 다)

리베이스 전의 커밋 포인터를 “이름 붙여” 고정해두면 복구가 극단적으로 쉬워집니다.

# 현재 PR 브랜치가 feature/login 이라고 가정

git checkout feature/login

# 로컬 백업 브랜치
git branch backup/feature-login-before-rebase

# 원격에도 백업 푸시(권장)
git push origin backup/feature-login-before-rebase

백업 브랜치를 원격에 올려두면, 로컬이 날아가도 GitHub에서 복구가 가능합니다.

3) 강제 푸시가 필요할 때는 --force-with-lease 사용

--force는 무조건 덮어쓰지만, --force-with-lease는 “내가 마지막으로 본 원격 상태”가 그대로일 때만 덮어씁니다. 즉, 누군가가 그 사이에 푸시했다면 실패해서 사고를 막아줍니다.

git push --force-with-lease origin feature/login

안전한 리베이스 패턴 1: PR 브랜치에서 직접 리베이스(최소 위험 버전)

언제 쓰나

  • PR 브랜치가 사실상 혼자 쓰는 브랜치
  • 리뷰어가 많지 않고, 히스토리 정리가 꼭 필요
  • 커밋 수가 많아 squash가 필요한 상황

절차

  1. 베이스 브랜치 최신화 후 리베이스
git fetch origin

git checkout feature/login

git rebase origin/main
  1. 인터랙티브 리베이스로 커밋 정리
git rebase -i origin/main
  1. 푸시(반드시 --force-with-lease)
git push --force-with-lease origin feature/login

주의점

  • 리뷰가 이미 많이 달린 PR이면, 커밋 리라이트는 리뷰 맥락을 무너뜨릴 수 있습니다. 이 경우 아래 “새 브랜치로 갈아타기” 패턴이 더 안전합니다.

안전한 리베이스 패턴 2: 새 브랜치로 리라이트하고 PR을 교체(가장 안전)

언제 쓰나

  • PR이 이미 공유되었고, 다른 사람이 내 브랜치를 기반으로 작업할 가능성이 있음
  • 리뷰 코멘트를 최대한 보존하고 싶음
  • 강제 푸시를 조직적으로 피하고 싶음

절차

  1. 현재 PR 브랜치에서 새 브랜치 생성
git fetch origin

git checkout feature/login

git checkout -b feature/login-v2
  1. 새 브랜치에서 리베이스 및 커밋 정리
git rebase origin/main

git rebase -i origin/main
  1. 새 브랜치 푸시
git push -u origin feature/login-v2
  1. GitHub에서 선택
  • 새 PR을 만든다(가장 깔끔)
  • 또는 기존 PR의 base/head를 바꾸는 대신, 보통은 새 PR 생성이 팀 커뮤니케이션에 안전합니다.

장점

  • 기존 PR은 그대로 남아 “증거/맥락”을 보존
  • 강제 푸시 없이도 정리된 히스토리를 제공

대참사 유형별 복구 패턴

1) 리베이스하다가 커밋이 사라진 것처럼 보일 때

대부분은 “사라진 게 아니라 브랜치 포인터가 이동했을 뿐”입니다. 이때 reflog가 생명줄입니다.

복구 절차: reflog로 리베이스 이전 HEAD 찾기

git reflog --date=local | head -n 30

출력에서 리베이스 시작 전의 항목을 찾습니다. 예를 들어 HEAD@{5}가 리베이스 전이라면:

# 즉시 되돌리기
git reset --hard HEAD@{5}

그 다음 원격도 되돌려야 한다면(리베이스 결과를 이미 푸시한 경우):

git push --force-with-lease origin feature/login

  • 리베이스 직후라면 ORIG_HEAD도 유용합니다.
git reset --hard ORIG_HEAD

2) git push --force를 해버려서 PR이 이상해졌을 때

이 경우도 대부분 복구 가능합니다. 핵심은 “원래 브랜치가 가리키던 커밋”을 찾아 브랜치 포인터를 되돌리는 것입니다.

복구 절차

  1. 원격에서 사라진 커밋도 로컬 reflog에 남아있을 가능성이 큼
git fetch origin

git reflog --date=local | head -n 50
  1. 되돌릴 커밋으로 reset
git reset --hard `원래-커밋-SHA`
  1. 원격 복구 푸시
git push --force-with-lease origin feature/login

만약 로컬에도 없으면

  • GitHub UI에서 PR의 “Commits” 탭, 또는 브랜치 보호/감사 로그, 또는 동료의 로컬에 남아있을 수 있습니다.
  • 그래서 “리베이스 전 백업 브랜치 원격 푸시”가 가장 강력한 보험입니다.

3) 리베이스 중 충돌 해결을 잘못해서 코드가 깨졌을 때

리베이스 충돌은 “한 번에 완벽히” 해결하려고 하면 실수하기 쉽습니다.

안전한 해결 루틴

  1. 충돌 확인
git status
  1. 충돌 파일을 열어 해결 후, 스테이징
git add path/to/file
  1. 계속 진행
git rebase --continue
  1. 도저히 모르겠으면 중단하고 백업으로 회귀
git rebase --abort

# 필요 시 백업 브랜치로 복원
git reset --hard backup/feature-login-before-rebase

팁: 충돌이 반복되면 rerere 활성화

반복 충돌을 학습해 다음에 자동 적용해주는 기능입니다.

git config --global rerere.enabled true

4) PR에 커밋이 중복으로 보이는 경우(merge base가 꼬임)

리베이스 후 강제 푸시를 했는데도 PR diff가 이상하게 커지고, 이미 머지된 커밋이 다시 보이는 현상이 있습니다. 보통은 base 브랜치가 바뀌었거나, 중간에 merge commit이 섞였거나, 브랜치가 잘못된 지점에서 갈라진 경우입니다.

진단 체크

git fetch origin

git log --oneline --decorate --graph --boundary origin/main..feature/login

git merge-base origin/main feature/login

복구 패턴: 깨끗한 브랜치로 체리픽

가장 확실한 방법은 “정상 기준점에서 새 브랜치 만들고 필요한 커밋만 옮기는 것”입니다.

git checkout -b feature/login-clean origin/main

# 필요한 커밋만 골라서 옮기기
git cherry-pick `커밋SHA1` `커밋SHA2`

git push -u origin feature/login-clean

그 다음 새 PR을 만들거나 기존 PR을 닫고 교체하는 편이 보통 더 빠릅니다.

팀 운영 관점: PR에서 리베이스를 허용할지 정책 정하기

권장 정책 조합

  • 개인 브랜치 PR: 리베이스 허용, 단 --force-with-lease 필수
  • 공동 작업 브랜치: 리베이스 금지(merge 또는 squash merge로 정리)
  • 보호 브랜치(main 등): 강제 푸시 금지, PR 머지 전략을 명시

GitHub에서 브랜치 보호 규칙으로 강제 푸시를 막고, 예외는 릴리즈/핫픽스 브랜치만 허용하는 식으로 운영하면 사고 확률이 급감합니다.

CI/CD가 브랜치 이벤트에 민감하다면(예: 특정 브랜치 push 시 배포), 리라이트가 배포 트리거를 중복 실행시키거나 롤백 포인트를 흐릴 수 있습니다. 이런 경우 배포 파이프라인 설계 관점에서 GitHub Actions OIDC로 AWS 키 없이 배포하기처럼 “어떤 이벤트를 신뢰할지”를 함께 정리해두는 게 좋습니다.

실전 체크리스트: 사고를 ‘복구 가능한 사고’로 만들기

리베이스 전

  • git fetch origin로 최신 상태 동기화
  • git branch backup/... 생성
  • git push origin backup/...로 원격 백업

리베이스 후

  • 로컬 테스트 및 git log --graph로 히스토리 확인
  • 푸시는 git push --force-with-lease
  • PR 코멘트로 “히스토리 리라이트 했음, 최신 커밋 기준으로 다시 리뷰 부탁” 공유

사고 시

  • 1순위: git reflog
  • 2순위: 원격 백업 브랜치
  • 3순위: 새 브랜치 생성 후 cherry-pick으로 재구성

결론

git rebase -i 자체가 위험한 게 아니라, 공유된 히스토리를 리라이트하면서도 되돌릴 장치를 마련하지 않는 것이 위험합니다. 백업 브랜치(로컬+원격), reflog 기반 복구, 그리고 --force-with-lease만 습관화해도 PR 대참사의 대부분은 “복구 가능한 이벤트”로 바뀝니다.

다음에 PR 히스토리를 정리해야 한다면, 먼저 백업 브랜치를 푸시하고 시작하세요. 그 10초가 몇 시간짜리 복구 작업을 없애줍니다.