Published on

Git rebase 후 강제푸시로 깨진 PR 복구 실전 가이드

Authors

서로 다른 히스토리를 가진 브랜치에 rebase를 수행한 뒤 --force 또는 --force-with-lease로 푸시하면, PR이 갑자기 깨진 것처럼 보이는 상황이 자주 발생합니다. 대표적으로는 다음 증상들이 나옵니다.

  • PR의 Files changed가 이상하게 늘거나(전체 파일 재검토 수준)
  • 기존 리뷰 코멘트가 Outdated로 대량 전환되거나
  • CI가 과거 커밋을 기준으로 돌다가 실패하거나
  • 팀원이 로컬에서 git pull 이후 브랜치가 꼬이거나
  • 심하면 특정 커밋이 “사라진 것처럼” 보입니다.

이 글은 “이미 강제푸시를 해버린 뒤”를 전제로, PR을 최대한 안전하게 복구하는 실전 절차를 정리합니다. 또한 같은 실수를 반복하지 않도록 재발 방지 체크리스트까지 포함합니다.

문맥상 함께 읽으면 좋은 글: Git rebase 후 강제푸시 없이 PR 정리하는 법

왜 PR이 깨지는가: 강제푸시의 본질

PR은 보통 “베이스 브랜치 기준으로, 소스 브랜치의 커밋 집합과 변경 파일”을 보여줍니다. rebase는 커밋을 “재작성”합니다. 즉, 같은 코드 변경이라도 커밋 SHA가 바뀝니다.

  • rebase 전: 커밋 A, B, C
  • rebase 후: 커밋 A’, B’, C’ (내용은 비슷해도 SHA는 완전히 다름)

여기서 강제푸시를 하면 원격 브랜치의 히스토리가 새 커밋들로 “교체”됩니다. PR 관점에서는 기존에 리뷰하던 커밋들이 더 이상 브랜치에 없으니, 코멘트가 끊기거나 diff 계산이 크게 흔들릴 수 있습니다.

특히 아래 상황에서 피해가 커집니다.

  • 여러 명이 같은 PR 브랜치에 추가 커밋을 올리는 협업 PR
  • 리뷰가 길어져 코멘트가 특정 라인에 많이 달린 PR
  • PR 브랜치가 다른 브랜치들의 베이스가 되는 경우

복구 목표를 먼저 선택하기

복구는 “무엇을 살릴 것인가”에 따라 접근이 달라집니다.

  1. 코드만 맞으면 된다: 리뷰 코멘트/히스토리 일부 손실 허용
  2. PR 리뷰 맥락을 최대한 보존: 코멘트가 달린 커밋/라인을 최대한 유지
  3. 팀원의 작업까지 보존: 다른 사람이 올린 커밋까지 정확히 복구

대부분의 팀에서는 2번과 3번이 중요합니다. 이 글은 2~3번을 우선하는 절차로 설명합니다.

0단계: 즉시 해야 할 안전 조치(더 망가지기 전에)

강제푸시 후 PR이 깨졌다면, 가장 먼저 “현재 원격 브랜치 상태를 보존”하세요.

# 원격 최신 상태를 가져온 뒤
git fetch origin

# 현재 원격 브랜치를 로컬에 백업 브랜치로 고정
git checkout -b backup/pr-broken origin/feature/my-pr

# 백업 브랜치를 원격에도 올려두면 더 안전
git push origin backup/pr-broken

이 백업은 이후 어떤 복구를 하든 “최악의 경우 돌아갈 지점”이 됩니다.

1단계: 무엇이 바뀌었는지 정확히 확인하기

원격 브랜치가 어디로 이동했는지 확인

git fetch origin

git log --oneline --decorate -n 20 origin/feature/my-pr

강제푸시 전 커밋을 찾기: reflog 활용

강제푸시로 덮어쓴 “이전 커밋”은 원격에서는 바로 접근이 어려울 수 있지만, 본인 로컬 또는 다른 팀원 로컬에는 흔적이 남아있을 가능성이 큽니다.

# 로컬에서 해당 브랜치의 이동 기록 확인
git reflog show feature/my-pr

# 혹은 HEAD 이동 기록
git reflog

reflog에서 강제푸시 직전의 커밋 SHA(예: old_sha)를 확보하는 것이 1차 목표입니다.

팁: 본인 로컬에 없다면, PR에 참여했던 팀원에게 아래를 요청하세요.

  • git reflog show feature/my-pr 결과
  • 또는 git log --oneline에서 강제푸시 전 마지막 커밋 SHA

GitHub UI에서 단서 찾기

  • PR 타임라인에 “force-pushed the branch” 이벤트가 남아있는 경우가 많습니다.
  • 강제푸시 직전 커밋 링크가 남아있거나, CI 빌드 로그에 커밋 SHA가 찍혀 있을 수 있습니다.

2단계: 복구 방법 선택(가장 자주 쓰는 3가지)

방법 A: 강제푸시 전 커밋으로 “되돌리기”(PR 맥락 보존 최우선)

가장 깔끔한 목표는 “강제푸시 전 상태로 브랜치를 되돌리는 것”입니다. 즉, PR이 깨지기 전 히스토리로 원격 브랜치를 다시 맞춥니다.

  1. 강제푸시 전 커밋을 찾았다고 가정합니다(old_sha).
git fetch origin

# 원격 PR 브랜치를 로컬로 가져옴
git checkout -b recover/pr origin/feature/my-pr

# 브랜치를 강제푸시 이전 커밋으로 되돌림
git reset --hard `old_sha`

# 안전장치가 있는 강제푸시 권장
git push --force-with-lease origin HEAD:feature/my-pr
  • --force-with-lease는 “내가 마지막으로 본 원격 상태에서 누가 또 푸시하지 않았는지”를 확인해줘서, 무작정 덮어쓰는 사고를 줄입니다.
  • 이 방식은 PR의 리뷰 코멘트/라인 매핑을 최대한 살릴 확률이 높습니다.

주의: 이미 강제푸시 이후에 누군가 추가 커밋을 올렸다면, 그 커밋은 사라질 수 있습니다. 이 경우 방법 C를 검토하세요.

방법 B: 새 브랜치로 PR을 다시 열기(코드 우선, 리뷰는 일부 손실)

이미 PR이 너무 꼬였거나, 원래 커밋을 찾기 어렵다면 “현재 코드 상태를 기준으로 새 PR”을 여는 게 비용 대비 효과가 좋을 때가 많습니다.

# 현재 원격 브랜치에서 새 브랜치 생성
git fetch origin

git checkout -b feature/my-pr-v2 origin/feature/my-pr

git push origin feature/my-pr-v2

이후 GitHub에서 새 PR을 열고, 기존 PR에는 다음을 남깁니다.

  • 왜 새 PR을 열었는지(강제푸시로 리뷰 맥락 손상)
  • 새 PR 링크
  • 기존 PR은 닫되, 이슈/티켓 링크는 유지

리뷰 코멘트가 많이 달렸던 PR이라면 아쉽지만, 팀 전체 시간이 더 절약되는 경우가 많습니다.

방법 C: 강제푸시 이후 커밋까지 포함해 “합치기”(협업 PR 복구)

문제는 보통 여기서 발생합니다.

  • A 방식으로 되돌리면: 강제푸시 이후에 팀원이 올린 커밋이 유실
  • 그대로 두면: PR 리뷰 맥락이 붕괴

이때는 “되돌린 옛 히스토리”와 “현재 원격 히스토리”를 모두 살리는 방향으로 복구합니다.

핵심은 두 커밋 집합을 각각 브랜치로 고정한 다음, 적절히 cherry-pick 또는 merge로 재구성하는 것입니다.

  1. 옛 상태(old_sha)와 현재 원격 상태를 각각 브랜치로 만듭니다.
git fetch origin

# 현재 원격 상태
git checkout -b state/new origin/feature/my-pr

# 옛 상태(강제푸시 전)
git checkout -b state/old `old_sha`
  1. 새 상태에만 있는 커밋을 찾아 옛 상태로 옮깁니다.
# old..new 범위에 있는(=new에만 있는) 커밋 목록 확인
git log --oneline state/old..state/new
  1. 옮길 커밋을 cherry-pick합니다.
# 복구 브랜치 생성(옛 상태 기반)
git checkout -b recover/combined state/old

# new에만 있는 커밋을 순서대로 체리픽
# 예: sha1, sha2, sha3
git cherry-pick `sha1` `sha2` `sha3`
  1. 원격 PR 브랜치에 반영합니다.
git push --force-with-lease origin recover/combined:feature/my-pr

이 방식은 PR 맥락과 협업 커밋을 모두 살릴 가능성이 높지만, 충돌 해결이 필요할 수 있습니다.

3단계: PR이 “변경 파일 전체가 뒤집히는” 문제를 줄이는 점검

강제푸시 후 PR에서 전체 파일이 변경된 것처럼 보이면, 보통 아래 원인이 많습니다.

  • 베이스 브랜치가 바뀌었거나(대상 브랜치 변경)
  • rebase 기준이 잘못되어 공통 조상이 달라졌거나
  • 포맷터/라인엔딩(CRLF)이 대량 변경을 만들었거나
  • merge commit을 rebase 과정에서 잘못 처리했거나

공통 조상 확인: merge-base

git fetch origin

# feature 브랜치와 base 브랜치의 공통 조상 확인
git merge-base origin/main origin/feature/my-pr

공통 조상이 의도와 다르면, PR diff가 비정상적으로 커질 수 있습니다. 이 경우 “올바른 베이스 커밋”을 기준으로 다시 rebase하거나, 아예 방법 A처럼 되돌리는 게 낫습니다.

diff 범위를 명시해서 확인

# main 대비 feature의 실제 변경을 확인
git diff --stat origin/main...origin/feature/my-pr

여기서 ...은 공통 조상을 기준으로 비교합니다. PR이 보여주는 변경과 유사한 관점이라 원인 파악에 좋습니다.

4단계: 팀원 로컬이 이미 꼬였을 때 가이드(필수 공지 템플릿)

브랜치를 강제푸시로 되돌리거나 재구성했다면, 팀원들은 로컬에서 일반 pull로는 해결이 안 될 수 있습니다. 아래처럼 공지하고, 안전한 동기화 절차를 안내하세요.

# 1) 로컬 변경사항이 있으면 먼저 백업(커밋 또는 스태시)
git status

git stash -u

# 2) 원격 최신 반영
git fetch origin

# 3) 로컬 브랜치를 원격과 동일하게 맞춤
git checkout feature/my-pr

git reset --hard origin/feature/my-pr

# 4) 스태시가 있었다면 필요한 것만 복원
git stash pop

팀원이 로컬에서 작업 중이던 커밋이 있다면, reset --hard 전에 별도 브랜치로 빼도록 안내하세요.

# 현재 작업을 임시 브랜치로 보존
git checkout -b wip/save-my-work

5단계: 재발 방지(강제푸시를 하더라도 사고 확률 줄이기)

1) --force 대신 --force-with-lease를 기본으로

git push --force-with-lease origin feature/my-pr

이 옵션 하나로 “남의 커밋을 모르고 덮어쓰기” 사고가 크게 줄어듭니다.

2) PR 브랜치 보호 규칙 적용

  • GitHub Branch protection에서 특정 브랜치 패턴에 대해 force push를 금지
  • 허용이 필요하다면, 특정 역할만 가능하도록 제한

3) rebase는 “개인 브랜치”에서만, 공유 브랜치에는 merge 전략

공유 PR 브랜치에서 히스토리 재작성은 비용이 큽니다. 팀 합의가 없다면 다음 규칙이 안전합니다.

  • 개인 작업 브랜치: 자유롭게 rebase
  • 공유 PR 브랜치: merge 또는 git revert 중심

4) PR 정리가 목적이라면, 강제푸시 없는 정리법 고려

강제푸시로 PR이 깨지는 근본 원인을 피하는 방법도 있습니다. 아래 글에서 “강제푸시 없이 PR을 정리하는 패턴”을 별도로 정리해두었습니다.

실전 시나리오별 빠른 처방전

시나리오 1: 혼자 작업한 PR인데 rebase 후 강제푸시로 코멘트가 다 깨짐

  • 목표: PR 맥락 복구
  • 처방: 방법 A로 reflog에서 old_sha 찾아 되돌리기

시나리오 2: 팀원이 같은 브랜치에 커밋을 올리는 협업 PR

  • 목표: 팀원 커밋 유실 방지
  • 처방: 방법 C로 옛 상태와 새 상태를 합쳐서 --force-with-lease

시나리오 3: 이미 히스토리가 너무 꼬이고 충돌이 감당 안 됨

  • 목표: 코드 전달 우선
  • 처방: 방법 B로 새 브랜치/새 PR, 기존 PR은 참조 링크 남기고 종료

마무리: 복구의 핵심은 “증거 보존”과 “목표 선택”

강제푸시로 PR이 깨졌을 때 가장 흔한 2차 사고는, 당황해서 다시 rebase/force push를 반복하며 복구 가능한 단서(reflog, CI 로그, 팀원 로컬)를 지워버리는 것입니다.

  • 먼저 백업 브랜치를 만들고
  • 강제푸시 전 커밋 SHA를 확보한 뒤
  • PR 맥락 보존(되돌리기) vs 코드 우선(새 PR) vs 협업 커밋 보존(재구성) 중 목표를 정하면

대부분의 케이스는 충분히 복구할 수 있습니다.