Published on

Git rebase 후 강제푸시? PR 깨짐 복구 7단계

Authors

서로 다른 팀에서 Git을 쓰다 보면, rebase--force 푸시 한 번으로 PR이 “깨진 것처럼” 보이는 순간이 옵니다. 커밋이 사라진 것 같고, 리뷰 코멘트가 엉뚱한 줄에 붙고, CI가 갑자기 실패하며, 머지 베이스가 바뀌어 diff가 폭증합니다.

핵심은 간단합니다. rebase는 커밋 해시를 바꾸고(역사가 재작성됨), PR은 “브랜치의 커밋 그래프”를 기준으로 비교를 계산합니다. 따라서 강제 푸시가 들어가면 PR이 망가진 게 아니라 PR이 참조하던 커밋들이 다른 커밋으로 교체된 상태가 됩니다.

이 글은 “무엇을 잃었는지”를 먼저 확인하고, 상황에 맞는 복구 루트를 선택해 안전하게 PR을 정상화하는 7단계를 제공합니다.

참고로, 장애를 원인별로 쪼개서 체크리스트로 복구하는 방식은 다른 운영 이슈에도 그대로 적용됩니다. 예: Chrome GPU Rasterization 꺼짐 원인과 복구 체크리스트

0) 왜 PR이 깨져 보이나: rebase와 force push의 본질

  • rebase는 커밋을 “다시 쌓는” 작업이라 커밋 해시가 바뀝니다.
  • git push --force 또는 git push --force-with-lease는 원격 브랜치 포인터를 새 커밋으로 강제로 옮깁니다.
  • PR 시스템(GitHub/GitLab)은 보통 base...head 비교로 diff를 만들고, 리뷰 코멘트는 특정 커밋/라인에 매핑됩니다.

그래서 다음 증상이 흔합니다.

  • PR diff가 갑자기 커짐(머지 베이스가 바뀜)
  • “커밋이 사라짐”처럼 보임(실제로는 해시가 바뀜)
  • 코멘트가 outdated로 바뀌거나 다른 줄로 이동
  • CI가 재실행되며 실패(환경 변화가 아니라 커밋 셋이 바뀌었기 때문)

이제부터는 “복구”를 단계적으로 진행합니다.

1단계: 더 이상 푸시하지 말고, 현재 상태를 스냅샷으로 고정

가장 먼저 할 일은 추가 손상을 막는 것입니다.

  1. 로컬에서 원격 최신을 가져옵니다.
git fetch origin --prune
  1. 현재 PR 브랜치(원격)를 로컬에 그대로 체크아웃합니다.
git switch -c pr-fix origin/feature/my-branch
  1. 안전 태그를 찍어둡니다(나중에 언제든 되돌릴 수 있게).
git tag backup/pr-broken-$(date +%Y%m%d-%H%M%S)

이 시점부터는 “무엇을 복구해야 하는지”를 확인합니다.

2단계: 잃어버린 커밋이 진짜로 사라졌는지 확인(reflog, remote)

대부분의 경우 커밋은 사라지지 않았고, 브랜치 포인터만 이동했을 뿐입니다.

2-1) 로컬 reflog로 이전 HEAD 찾기

git reflog --date=iso

출력에서 rebase 직전의 HEAD@{n} 또는 “force push 이전”으로 보이는 지점을 찾습니다.

해당 커밋을 확인합니다.

git show HEAD@{3}

2-2) 원격 브랜치의 이전 위치 추적

원격 추적 브랜치도 reflog가 남아 있을 수 있습니다.

git reflog show origin/feature/my-branch --date=iso

여기서 “강제 푸시 직전” 커밋 해시를 확보하면, 복구는 사실상 끝난 겁니다.

3단계: PR이 깨진 유형을 분류(복구 루트 선택)

복구 방법은 크게 3가지로 갈립니다.

  1. PR 브랜치가 잘못 rebase 됨: 커밋이 다른 베이스 위로 재작성되어 diff 폭증
  2. 커밋 일부가 누락됨: interactive rebase 중 drop, fixup 실수
  3. 리뷰/코멘트만 깨짐: 코드 상태는 맞는데 커밋 해시가 바뀌어 코멘트가 outdated

각 케이스에 따라 4~6단계를 다르게 선택합니다.

4단계: 가장 안전한 복구 1안 - 이전 커밋으로 브랜치 되돌리기(reset) 후 강제 푸시

“강제 푸시 직전 커밋”을 알고 있고, 그 상태가 정답이었다면 브랜치를 그 지점으로 되돌리는 게 가장 빠릅니다.

4-1) 이전 커밋으로 하드 리셋

아래에서 OLD_SHA는 reflog에서 찾은 커밋 해시입니다.

git switch pr-fix
git reset --hard OLD_SHA

4-2) 원격에 안전하게 강제 푸시하기

가능하면 --force-with-lease를 씁니다. 이 옵션은 “내가 마지막으로 봤던 원격 상태”에서 누군가 또 푸시했으면 실패하게 만들어, 타인의 작업을 덮어쓰는 사고를 줄입니다.

git push --force-with-lease origin HEAD:feature/my-branch

이렇게 하면 PR은 대체로 정상으로 돌아옵니다.

  • diff가 원래대로
  • 커밋 목록이 원복
  • CI도 원래 커밋 기준으로 재평가

단, 리뷰 코멘트는 일부 outdated로 남을 수 있습니다(커밋/라인 매핑이 바뀌었기 때문). 이건 “복구 불가”라기보다 PR 시스템 특성입니다.

5단계: 안전한 복구 2안 - 누락된 커밋만 골라서 복원(cherry-pick)

rebase 과정에서 커밋 몇 개가 빠졌다면, 전체를 되돌리기보다 누락 커밋만 다시 얹는 방식이 깔끔합니다.

5-1) 누락 커밋 찾기

현재 브랜치와 “정상으로 보이는 커밋”이 있는 브랜치/태그를 비교합니다.

git log --oneline --decorate --graph --all --max-count=50

또는 특정 커밋이 포함되어 있는지 확인합니다.

git branch --contains SOME_SHA

5-2) cherry-pick으로 복원

git switch pr-fix
git cherry-pick A_SHA B_SHA C_SHA

충돌이 나면 해결 후 계속 진행합니다.

git status
# 충돌 해결 후
git add -A
git cherry-pick --continue

마지막으로 원격에 푸시합니다(이 경우는 히스토리가 이미 바뀐 상태일 수 있어 강제 푸시가 필요할 수 있음).

git push --force-with-lease origin pr-fix:feature/my-branch

6단계: 안전한 복구 3안 - PR은 새 브랜치로 살리고, 기존 브랜치는 고정(권장되는 협업 전략)

팀 협업에서 가장 분쟁이 적은 방식은 기존 PR 브랜치는 더 이상 건드리지 않고, 새 브랜치로 PR을 다시 여는 것입니다.

특히 다음 상황이면 이 방식을 추천합니다.

  • 리뷰 코멘트가 많이 달려 있고, 해시 변경이 잦아 리뷰가 계속 깨짐
  • 이미 다른 사람이 해당 브랜치를 기반으로 작업 중
  • CI/배포 파이프라인이 브랜치 이벤트에 민감

6-1) 깨진 상태를 보존하는 브랜치 만들기

git fetch origin
git switch -c feature/my-branch-broken origin/feature/my-branch
git push origin feature/my-branch-broken

6-2) 정상 커밋(또는 복구한 커밋)으로 새 브랜치 생성

git switch -c feature/my-branch-recovered OLD_SHA
# 또는 cherry-pick으로 복구한 pr-fix에서
# git switch -c feature/my-branch-recovered pr-fix

git push -u origin feature/my-branch-recovered

그 다음 PR을 새 브랜치로 다시 생성하고, 기존 PR에는 “새 PR 링크”와 함께 종료 사유를 남깁니다.

이 접근은 “역사 재작성”을 최소화해, PR/리뷰/자동화가 받는 충격을 줄입니다.

7단계: 재발 방지 체크리스트(강제 푸시를 완전히 없애지 못한다면)

강제 푸시는 때로 필요합니다(정리된 커밋, 보안 이슈로 인한 히스토리 제거 등). 다만 “사고 확률”을 낮추는 규칙이 중요합니다.

7-1) --force 대신 --force-with-lease 기본화

git push --force-with-lease

팀에서 alias로 강제하는 것도 방법입니다.

git config --global alias.fpush "push --force-with-lease"

7-2) 공유 브랜치에서는 rebase 금지, 개인 브랜치에서만 허용

  • main, develop, 릴리즈 브랜치: merge 기반
  • 개인 작업 브랜치: rebase 허용

GitHub라면 브랜치 보호 규칙으로 “force push 금지”를 켜는 게 가장 확실합니다.

7-3) PR 올리기 전, 로컬에서 베이스와의 관계를 확인

내 브랜치가 어떤 베이스를 기준으로 쌓였는지 확인합니다.

git merge-base origin/main HEAD

또는 로그 그래프로 “내가 의도한 베이스 위에 있는지” 확인합니다.

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

7-4) CI가 깨졌다면 “코드 문제”와 “히스토리 변경”을 분리해서 진단

강제 푸시 직후 CI 실패는 코드 회귀일 수도 있지만, 종종 다음처럼 “기준점 변경”이 원인입니다.

  • lockfile이 다른 커밋과 함께 움직이며 의존성이 바뀜
  • base branch가 바뀌어 테스트 대상이 달라짐
  • 스쿼시 과정에서 환경설정 파일이 누락

이런 식의 원인 분리 사고는 다른 운영 트러블슈팅에도 유효합니다. 예를 들어, 네트워크 타임아웃도 증상만 보면 단일 문제처럼 보이지만 실제로는 원인별로 접근해야 빠르게 해결됩니다: Go gRPC DEADLINE_EXCEEDED 원인별 해결 7가지

자주 겪는 Q&A

Q1. 이미 --force로 덮어쓴 커밋은 복구 불가능한가요?

대부분은 복구 가능합니다.

  • 로컬에 남아 있으면 git reflog에서 찾을 수 있습니다.
  • 다른 팀원의 로컬에도 남아 있을 수 있습니다.
  • Git 서버가 GC를 돌리기 전이라면 원격에서도 흔적이 남아 있을 수 있습니다.

단, 시간이 오래 지나고 모든 클론에서 참조가 끊기면 GC로 수거될 수 있으니, 사고 직후가 골든타임입니다.

Q2. PR 코멘트가 다 깨졌는데 되살릴 수 있나요?

커밋 해시가 바뀌면 코멘트 매핑이 바뀌는 건 PR 시스템의 구조적 한계라 “완전 복구”는 어렵습니다. 대신 다음을 선택합니다.

  • 코드 상태가 중요하면 4단계로 커밋을 원복해 코멘트 손실을 최소화
  • 이미 해시가 크게 바뀌었다면 6단계처럼 새 PR로 리뷰를 새로 받는 게 더 빠름

Q3. 팀에서 rebase를 아예 금지해야 하나요?

금지까지는 과할 수 있지만, 공유 브랜치에서의 rebase와 force push는 금지하는 게 일반적으로 안전합니다. 개인 브랜치에서 커밋 정리 후 PR을 올리는 흐름이면 rebase의 장점(깔끔한 히스토리)을 살리면서도 사고 범위를 제한할 수 있습니다.

마무리: “되돌릴 수 있는 강제 푸시”가 핵심

PR이 깨져 보이는 상황의 대부분은 “커밋이 삭제됨”이 아니라 “그래프가 재작성됨”입니다. 복구는 다음 순서로 접근하면 안전합니다.

  1. 추가 푸시 중단, 스냅샷(태그) 고정
  2. reflog로 이전 커밋 확보
  3. 유형 분류
  4. 원복이 최선이면 reset --hard--force-with-lease
  5. 일부 누락이면 cherry-pick
  6. 협업 비용이 크면 새 브랜치/새 PR
  7. 재발 방지 규칙 적용

이 7단계를 팀 런북으로 만들어 두면, 강제 푸시 사고가 나도 “누가 뭘 망쳤는지”가 아니라 “어떤 절차로 복구할지”로 대화가 바뀝니다.