- Published on
Git rebase 후 강제푸시 없이 PR 살리는 법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론부터 결론까지 한 줄로 요약하면 이렇습니다. rebase는 커밋을 “새로 만든다”(SHA 변경) → 기존 PR 브랜치와 히스토리가 갈라짐 → 보통은 --force-with-lease로 덮어씌우지만, 정책/권한/리스크 때문에 강제푸시가 막혀 있는 팀도 많습니다. 이 글에서는 강제푸시 없이도 PR을 유지하거나 새 PR로 안전하게 이어가는 방법들을 GitHub 중심으로 정리합니다.
> 참고로 빌드/테스트 파이프라인이 PR 단위로 캐시/아티팩트를 재사용하는 팀이라면, PR을 “살리는” 과정에서 CI가 다시 전부 도는 문제가 생길 수 있습니다. 이런 경우는 GitHub Actions 캐시 안 먹힘 원인 7가지도 같이 보면 좋습니다.
왜 rebase 후 PR이 망가지는가
git rebase는 단순히 커밋을 “옮기는” 게 아니라, 기존 커밋을 기반 위에서 다시 적용하며 새로운 커밋을 생성합니다.
- 기존 커밋:
A - B - C(각각 SHA:a1, b1, c1) - rebase 후 커밋:
A - B' - C'(SHA:b2, c2)
이때 원격 PR 브랜치(예: origin/feature)에는 여전히 b1, c1이 있고, 로컬에는 b2, c2가 생깁니다.
- 강제푸시를 하면: 원격 브랜치를 로컬 히스토리로 “재작성”
- 강제푸시를 못 하면: 원격 브랜치의 히스토리를 유지한 채로, 새 히스토리를 다른 방식으로 반영해야 함
목표별 전략 지도
상황에 따라 최적해가 다릅니다.
- PR 번호/토론/리뷰 코멘트를 그대로 유지하고 싶다 → 가능하면 “원격 브랜치를 업데이트”해야 함(하지만 강제푸시 금지)
- PR은 새로 파도 된다. 대신 변경사항만 정확히 옮기고 싶다 → 새 브랜치/새 PR이 가장 안전
- 이미 rebase는 했고, 로컬에만 새 커밋이 있다. 원격 PR 브랜치에는 옛 커밋이 있다 →
cherry-pick,merge --no-ff, “rebase를 되돌린 뒤 merge” 등 선택
이 글의 핵심은 1번을 “완벽히” 해결하기 어렵다는 점입니다. rebase 결과를 같은 브랜치에 반영하려면 원칙적으로 fast-forward가 불가능하기 때문입니다. 강제푸시 없이 PR을 “완전히 동일한 브랜치로 살리는” 건 제약이 큽니다. 대신 아래 패턴들로 리스크 없이 PR을 유지하거나, PR 컨텍스트를 최대한 보존할 수 있습니다.
방법 1) (가장 안전) 새 브랜치로 PR을 새로 만들고, 기존 PR은 연결만 남긴다
강제푸시가 금지된 조직에서 현실적으로 가장 많이 쓰는 방법입니다.
절차
- 현재 PR 브랜치(원격)를 기준으로 새 브랜치를 따고,
- rebase로 만들어진 “새 커밋들”을
cherry-pick으로 옮긴 뒤, - 새 PR을 만들고 기존 PR에는 링크/설명을 남깁니다.
# 1) 최신 상태 동기화
git fetch origin
# 2) 기존 PR 브랜치에서 새 브랜치 생성
# 예: 기존 PR 브랜치가 feature/login
git switch -c feature/login-v2 origin/feature/login
# 3) rebase로 새로 만들어진 커밋들이 있는 로컬 브랜치로 이동
# 예: rebase 결과가 feature/login-rebased
git switch feature/login-rebased
# 4) 옮길 커밋 범위를 확인
git log --oneline --decorate --graph
# 5) 새 브랜치로 돌아가서 커밋들을 cherry-pick
git switch feature/login-v2
# 예: 커밋 3개를 연속으로 옮긴다면
git cherry-pick <oldest_sha>^..<newest_sha>
# 6) 푸시 후 새 PR
git push -u origin feature/login-v2
장점
- 강제푸시 불필요
- 브랜치 보호 규칙/감사 정책 준수
- 기존 PR의 토론/리뷰는 남기고, 새 PR로 깔끔하게 진행 가능
단점
- PR 번호가 바뀜(하지만 기존 PR에 “Superseded by #1234” 식으로 연결하면 관리 가능)
운영 팁
- 기존 PR을 닫지 말고 “Draft”로 전환하거나, 제목에
[Superseded]를 붙여 추적성을 남기세요. - 새 PR 본문에 기존 PR 링크를 명시하고, 변경 요약/리스크/테스트 결과를 다시 정리하면 리뷰어 피로가 줄어듭니다.
방법 2) 기존 PR 브랜치에 rebase 결과를 “merge commit”으로 합쳐서 강제푸시를 피한다
핵심 아이디어는 이겁니다.
- rebase 결과는 기존 브랜치와 공통 조상은 같지만 히스토리가 갈라진 다른 라인
- 강제푸시 없이 원격 브랜치에 반영하려면 fast-forward가 아닌 merge로 합칠 수밖에 없음
- 즉, “rebase된 브랜치”를 “기존 PR 브랜치”에 merge commit으로 합쳐 PR을 진행
절차
git fetch origin
# 기존 PR 브랜치 체크아웃 (원격 기준)
git switch feature/login
# rebase 결과 브랜치(로컬)에 있는 커밋을 merge로 합침
# --no-ff를 주면 merge commit이 만들어져 히스토리가 명확해짐
git merge --no-ff feature/login-rebased
# 충돌 해결 후
git push origin feature/login
장점
- 기존 PR 브랜치(그리고 PR 번호)를 유지 가능
- 강제푸시 불필요
단점(중요)
- 히스토리가 “예쁘게” 선형이 되지 않습니다. rebase의 장점(선형 히스토리)이 사라질 수 있음
- 경우에 따라 동일 변경이 중복 적용된 것처럼 보이거나(특히 부분적으로 이미 반영된 경우), 리뷰 diff가 커질 수 있음
언제 쓰면 좋은가
- 팀이 “선형 히스토리 강제”가 아니고, merge commit을 허용할 때
- PR 번호/토론을 반드시 유지해야 할 때
방법 3) rebase를 되돌려 fast-forward 가능 상태로 만든다(로컬에서만)
이미 로컬에서 rebase를 했지만, 원격 브랜치를 살리고 싶고 강제푸시가 금지라면, 로컬에서 rebase를 “없던 일”로 만들고(즉 원격과 동일한 커밋 그래프로 복귀), 그 다음엔 정상적인 방식으로 base 브랜치 변경을 따라가야 합니다.
여기서 포인트는 “rebase 결과를 원격에 반영”하는 게 아니라,
- 로컬을 원격과 동일하게 되돌린 다음
- base 브랜치 변경분은
merge또는pull --rebase가 아닌 다른 방식으로 반영
절차(로컬 브랜치 복구)
git fetch origin
# 현재 작업 브랜치가 feature/login 이라고 가정
# reflog로 rebase 이전 커밋을 찾는다
git reflog --date=local | head -n 20
# 예: rebase 시작 직전이 HEAD@{7} 이었다면
git reset --hard HEAD@{7}
# 원격과 동일한지 확인
git log --oneline --decorate --graph --max-count=20
이후 base 브랜치(main)의 변경을 가져오는 방식은 팀 정책에 맞춰 선택합니다.
- merge 허용 팀:
git merge origin/main - 선형 히스토리 강제 팀: 원칙적으로 rebase가 필요한데, 이 경우 결국 강제푸시가 필요해집니다. 즉 “강제푸시 없이 선형 유지”는 구조적으로 충돌합니다.
장점
- PR 브랜치가 원격과 다시 일치하므로 혼란이 줄어듦
단점
- rebase로 정리해둔 커밋 스택을 포기해야 함
방법 4) 커밋을 “패치”로 만들어 적용한다(복잡한 상황에서 유용)
cherry-pick이 충돌을 과도하게 만들거나, 커밋 범위가 애매할 때는 패치 기반이 더 명시적일 수 있습니다.
git fetch origin
# rebase된 브랜치에서 패치 생성
git switch feature/login-rebased
git format-patch origin/main..HEAD -o /tmp/patches
# 기존 PR 브랜치 기반의 새 브랜치에 적용
git switch -c feature/login-v2 origin/feature/login
git am /tmp/patches/*.patch
git push -u origin feature/login-v2
git am은 메일 패치 적용 방식이라 커밋 메시지/작성자/순서를 비교적 잘 보존합니다.
GitHub PR을 “살린 것처럼” 보이게 하는 실무 운영 팁
기술적으로 PR 번호를 유지하지 못하는 경우가 많아서, 운영으로 커버하는 방법이 중요합니다.
1) 기존 PR 코멘트/리뷰를 새 PR로 이관하는 체크리스트
- 기존 PR Description을 새 PR에 복사하되, 맨 위에 다음을 추가
- 기존 PR 링크
- 왜 rebase/브랜치 재생성이 필요했는지
- 변경사항 요약(리뷰어가 다시 전체를 읽지 않게)
- 테스트 결과/재현 방법
2) CI 비용 최적화
PR이 새로 만들어지면 캐시 키가 달라져 캐시 미스가 날 수 있습니다. 캐시 키를 브랜치명/PR번호에 강하게 묶어두면 이런 일이 잦습니다. 캐시 설계가 흔들릴 때는 GitHub Actions 캐시 안 먹힘 원인 7가지를 참고해 키 전략을 점검하세요.
3) “왜 강제푸시가 금지인가”를 팀 합의로 문서화
강제푸시 금지는 보통 아래 이유 때문입니다.
- 감사/추적성(누가 무엇을 언제 바꿨는지)
- 리뷰 코멘트가 특정 커밋 SHA에 달라붙어 무효화되는 문제
- 다른 사람이 같은 브랜치에서 작업 중일 때 히스토리 재작성으로 인한 혼란
이런 맥락은 장애 대응/재현성 관점과도 닿아 있습니다. 예를 들어 운영 장애에서 원인 추적이 중요한 것처럼(예: 리눅스 OOM Killer 로그로 원인 프로세스 찾기), 코드 변경 이력도 “관찰 가능성”이 중요합니다.
자주 겪는 케이스별 처방
케이스 A: rebase를 이미 했고, 원격 푸시가 거부된다(브랜치 보호)
- 정답: 방법 1(새 브랜치/새 PR)
- 차선: merge commit 허용이면 방법 2
케이스 B: PR 브랜치에 다른 사람이 커밋을 추가했다
- 강제푸시 금지 환경에서 가장 위험한 상황
- 권장: 원격 PR 브랜치를 기준으로 새 브랜치 생성 → 필요한 커밋만 cherry-pick → 새 PR
케이스 C: 리뷰 코멘트를 유지해야 한다(커밋 단위 코멘트가 많음)
- 현실적으로 완벽 보존은 어렵습니다.
- 차선책:
- 기존 PR을 닫지 않고 유지
- 새 PR에서 “이 코멘트는 기존 PR의 특정 라인에 있었고, 여기로 이동했다”를 요약
- 가능하면 커밋을 크게 바꾸지 말고(파일 이동/대규모 포맷팅 최소화) diff 안정성을 확보
결론: “강제푸시 없이 rebase 결과를 같은 PR에 반영”은 대체로 불가능, 대신 안전한 우회로를 택하자
정리하면 다음 우선순위가 실전에서 가장 덜 아프게 갑니다.
- 새 브랜치 + 새 PR(cherry-pick 또는 patch)로 안전하게 이어가기
- PR 번호를 꼭 유지해야 하고 merge commit 허용이면, rebase 결과를 기존 PR 브랜치에 merge
- 로컬 rebase를 되돌려 원격과 맞춘 뒤, 팀 정책에 맞는 방식으로 base 변경을 반영
원한다면, 현재 상황을 몇 줄만 알려주면(예: GitHub/Bitbucket, 브랜치 보호 규칙, “merge commit 허용 여부”, 이미 원격에 푸시된 커밋 유무) 그 조건에서 가장 깔끔한 커맨드 시퀀스로 재구성해 드릴 수 있습니다.