Published on

Git rebase 후 강제푸시 없이 PR 정리하는 법

Authors

서론

git rebase는 커밋을 예쁘게 만들고(불필요한 merge 커밋 제거, 커밋 메시지 정리, squash 등) 리뷰 부담을 줄이는 데 매우 유용합니다. 문제는 이미 PR을 올린 브랜치에서 rebase를 해버리면 커밋 SHA가 바뀌고, 원격 브랜치를 업데이트하려면 보통 강제푸시(force push) 가 필요하다는 점입니다.

하지만 현실에서는 다음 같은 이유로 강제푸시가 금기이거나 불가능한 경우가 많습니다.

  • 보호 브랜치/정책으로 force push가 막혀 있음
  • 리뷰 중인 PR에서 커밋이 사라지거나 코멘트 위치가 어긋나는 것을 싫어함
  • CI/배포 파이프라인이 “이력 변경”에 민감함
  • 여러 사람이 같은 feature 브랜치를 공유하고 있음

이 글에서는 rebase를 이미 해버렸거나, rebase를 하고 싶지만 강제푸시 없이 PR을 깔끔하게 유지해야 할 때 쓸 수 있는 패턴을 정리합니다. 핵심은 “PR의 대상이 되는 브랜치를 바꾸지 않고도(= force push 없이도) 결과를 반영하는 방법”을 선택하는 것입니다.

> 참고: CI가 PR 기준으로 돌아가고, 캐시가 꼬여 빌드 시간이 갑자기 늘어나는 상황도 종종 겪습니다. 이럴 땐 캐시 키/경로/권한을 점검하는 글도 함께 보면 좋습니다: GitHub Actions 캐시가 안 먹을 때 키·경로·권한


왜 rebase 후에는 force push가 필요한가

rebase는 커밋을 “재작성”합니다. 즉, 동일한 변경 내용이라도 커밋 객체가 새로 만들어져 SHA(해시)가 달라집니다.

  • PR 브랜치가 origin/feature이고,
  • 로컬에서 git rebase main을 하면,
  • 로컬 feature는 원격 origin/feature완전히 다른 커밋 그래프를 갖게 됩니다.

이 상태에서 일반 푸시(git push)를 하면 “non-fast-forward”로 거절되고, 업데이트하려면 --force 또는 --force-with-lease가 필요합니다.

그럼 강제푸시 없이 PR을 정리하려면?

  • 기존 PR 브랜치(원격)를 바꾸지 않는다
  • 대신 새 브랜치를 만들거나, merge 기반으로 PR을 최신화하거나, PR을 대체하는 새 PR을 만든다

이 중 어떤 전략이 팀 정책과 상황에 맞는지 선택하면 됩니다.


전략 1) “새 브랜치 + 새 PR”로 갈아타기 (가장 안전)

강제푸시가 금지인 팀에서 가장 깔끔하고 사고가 적은 방법은 rebase된 결과를 새 브랜치로 푸시하고 새 PR을 만드는 것입니다.

언제 쓰나

  • force push가 정책상 불가
  • 기존 PR에서 리뷰 코멘트가 많이 달렸지만, 커밋 히스토리를 정리한 버전을 새로 리뷰받고 싶음
  • 기존 PR이 꼬였거나(충돌/이력 혼란) CI가 이상하게 도는 상황

절차

  1. 현재 PR 브랜치(원격 기준)를 보존한 채, 로컬에서 rebase된 HEAD를 새로운 브랜치로 분기합니다.
# PR 브랜치가 feature 라고 가정

git checkout feature

git fetch origin

git rebase origin/main

# 새 브랜치 생성

git checkout -b feature-rebased

# 새 브랜치를 원격에 푸시

git push -u origin feature-rebased
  1. GitHub에서 feature-rebased로 새 PR을 생성합니다.

  2. 기존 PR은 다음 중 하나로 처리합니다.

  • Close: 새 PR 링크를 남기고 종료
  • Draft로 전환: 기록 보존 목적

장점

  • 기존 PR 히스토리/코멘트가 깨지지 않음
  • 정책(보호 규칙)과 충돌하지 않음
  • “지금부터 리뷰할 대상”이 명확해짐

단점

  • PR이 2개가 되어 관리 비용이 늘 수 있음
  • 기존 PR 코멘트를 새 PR로 옮겨야 할 수 있음

> 팁: 새 PR 설명에 “기존 PR 링크, 변경 이유, 리뷰어가 다시 봐야 할 포인트”를 요약하면 리뷰 비용을 크게 줄일 수 있습니다.


전략 2) 기존 PR은 유지하고, rebase 결과는 “merge 커밋”으로 따라잡기

조금 역설적이지만, rebase를 했더라도 PR 브랜치에는 rebase 결과를 ‘그대로’ 강제푸시할 수 없으니, PR 브랜치를 최신 mainmerge해서 충돌을 해결하고 진행하는 방식입니다.

즉, “히스토리를 예쁘게 만들기 위해 rebase”를 했는데, PR 브랜치에는 merge 커밋이 생길 수 있습니다. 하지만 팀이 “강제푸시 금지, 대신 merge 허용”이라면 현실적인 타협안이 됩니다.

절차

# 원격 최신화

git fetch origin

# PR 브랜치로 이동

git checkout feature

# main을 merge해서 최신화

git merge origin/main

# 충돌 해결 후 커밋

git push origin feature

장점

  • 기존 PR을 그대로 유지(링크/코멘트/체크 유지)
  • force push 불필요

단점

  • 커밋 히스토리가 복잡해질 수 있음
  • rebase로 정리한 커밋 메시지/구조가 PR 브랜치에 반영되지 않음(결과만 반영)

이 방식은 “PR을 깨끗하게 보이게 만들겠다”는 목표보다는 “PR을 안전하게 최신화하고 머지 가능 상태로 만들겠다”는 목표에 더 적합합니다.


전략 3) rebase된 커밋을 cherry-pick으로 새 브랜치에 재구성

전략 1(새 브랜치 + 새 PR)과 비슷하지만, rebase를 이미 해버렸고 그 결과에서 일부 커밋만 골라 PR을 새로 구성하고 싶을 때 유용합니다.

절차

  1. 기준 브랜치에서 새 브랜치 생성
git fetch origin

git checkout -b feature-clean origin/main
  1. 기존 브랜치(또는 rebase된 로컬 브랜치)에서 필요한 커밋만 골라 적용
# 커밋 여러 개를 연속으로 가져오기(범위)
# A^..B 는 A부터 B까지 포함

git cherry-pick A^..B

# 또는 개별 커밋 선택

git cherry-pick <commit1> <commit2> <commit3>
  1. 푸시 후 새 PR
git push -u origin feature-clean

장점

  • PR에 포함될 커밋을 “의도대로” 정제 가능
  • 불필요한 커밋(실험/되돌림/로그 등)을 제거하기 쉬움

단점

  • 충돌이 날 수 있고, 해결 비용이 발생
  • 커밋 수가 많으면 번거로움

전략 4) PR을 “스쿼시 머지”로 끝내고, 브랜치 히스토리 정리는 포기

팀 정책이 main의 히스토리만 중요하고 PR 브랜치 히스토리는 덜 중요하다면, rebase를 굳이 PR 브랜치에 반영하지 않고:

  • PR은 리뷰가 끝나는 대로
  • GitHub의 Squash and merge로 머지

하는 것도 실무에서 흔한 선택입니다.

언제 좋나

  • 커밋 히스토리를 main에서만 관리하고 싶다
  • PR 브랜치 정리보다 “리뷰/검증된 단일 변경”이 중요하다

주의점

  • PR 브랜치의 커밋이 지저분해도 main은 깔끔해짐
  • 다만 PR 리뷰 중 “커밋 단위 리뷰”가 필요한 팀에는 맞지 않을 수 있음

실전에서 가장 많이 하는 조합: “force-with-lease 허용 + 예외 최소화”

질문 주제는 “강제푸시 없이”지만, 팀 차원에서 장기적으로는 완전 금지보다 --force-with-lease만 제한적으로 허용하는 쪽이 오히려 안전한 경우가 많습니다.

  • --force는 원격의 누군가 작업을 덮어쓸 수 있음
  • --force-with-lease는 “내가 마지막으로 본 원격 상태 그대로일 때만” 덮어씀
# 안전장치가 있는 강제푸시

git push --force-with-lease origin feature

다만 조직 정책상 이게 불가하다면, 이 글의 전략 1(새 PR) 또는 전략 2(merge로 최신화)가 표준 운영 절차가 됩니다.


PR을 깔끔하게 유지하는 팀 규칙(강제푸시 없이도 효과 있음)

강제푸시를 하지 않더라도 PR이 “정리돼 보이게” 만드는 규칙들이 있습니다.

1) PR을 작게 쪼개기

  • rebase가 필요한 이유(커밋/변경이 너무 많음)를 줄여줍니다.
  • 충돌 해결 비용도 감소합니다.

2) 커밋 메시지 규칙 + PR 템플릿

  • 커밋이 지저분해도 PR 설명이 명확하면 리뷰가 쉬워집니다.
  • What/Why/How, 테스트 방법, 롤백 방법을 템플릿화하세요.

3) “리뷰 중에는 rebase 금지, 머지 직전에만 허용”

  • 리뷰 코멘트가 달린 라인이 rebase로 이동하면 맥락이 깨집니다.
  • 머지 직전에만 정리하면 충돌/혼란이 줄어듭니다.

상황별 추천 의사결정표

  • force push 완전 금지 + 이미 rebase 해버림: 전략 1(새 브랜치/새 PR)
  • force push 금지지만 PR 링크/코멘트를 유지해야 함: 전략 2(merge로 최신화)
  • 커밋 일부만 골라서 깔끔한 PR이 필요: 전략 3(cherry-pick)
  • main만 깔끔하면 됨 + 빠르게 끝내기: 전략 4(squash merge)

자주 하는 실수와 체크리스트

실수 1) rebase 후 로컬 브랜치만 믿고 PR이 ‘업데이트된 줄’ 착각

  • PR은 원격 브랜치 기준입니다.
  • 로컬에서 아무리 rebase해도 원격을 갱신하지 않으면 PR은 그대로입니다.
# PR이 실제로 어떤 커밋을 보고 있는지 확인

git fetch origin

git log --oneline --decorate --graph origin/feature -n 20

실수 2) 새 PR로 갈아탔는데 CI/캐시가 꼬여 빌드가 느려짐

실수 3) cherry-pick으로 가져오다 테스트/포맷 커밋을 누락

  • 범위를 고를 때 “의존 커밋”이 빠지지 않았는지 확인해야 합니다.
# 어떤 커밋이 어떤 파일을 건드렸는지 확인

git show --name-only <commit>

결론

git rebase는 좋은 도구지만, PR이 이미 열려 있고 강제푸시가 제한된 환경에서는 “히스토리를 재작성한 결과를 같은 PR에 반영”하기가 구조적으로 어렵습니다. 이때는 억지로 한 PR을 살리려 하기보다, 다음 중 하나를 의도적으로 선택하는 게 PR 품질과 팀 생산성을 동시에 지키는 길입니다.

  • 가장 안전: 새 브랜치로 푸시 → 새 PR로 교체
  • 가장 보수적: 기존 PR 유지 → main을 merge해서 최신화
  • 가장 정제된 PR: 새 브랜치에서 cherry-pick으로 재구성
  • 가장 빠른 종료: squash merge로 main만 깔끔하게

팀 정책이 허용한다면 장기적으로는 --force-with-lease를 제한적으로 허용하는 것도 고려해볼 만하지만, 그렇지 않더라도 위 전략들만으로 충분히 “강제푸시 없이 PR을 정리”할 수 있습니다.