본문 바로가기

노트/이모저모

[git] 브랜치 조상 관계 이슈로 PR이 생성되지 않는 문제 해결하기 (git rebase와 cherry pick)

git rebase

main, feature 브랜치가 있고 feature가 main에서 나온 브랜치라고 치자.

rebase는 브랜치의 커밋 전체가 합쳐지지만, main의 새 기준점 위로 다른 커밋 SHA를 가지고 쌓인다.

git checkout feature
git rebase main          # feature의 커밋들을 최신 main 위로 재배치
git push --force-with-lease   # 이미 push했다면 필요 (공유 브랜치는 주의)

 

*참고: --force-with-lease:

내가 마지막으로 확인한 원격 상태 그대로일 때만 강제 push 하겠다는 옵션

rebase나 cherry-pick, reset --hard처럼 히스토리를 바꾼 뒤에는 일반 push가 거부되기 때문에 강제 push 해야한다.

여러 명이 함께 쓰는 브랜치에는 force push하지 않는 것이 원칙이므로, 내가 혼자 쓰는 feature 브랜치를 rebase한 뒤 올릴 때 --force 대신 사용하는 것이 좋다.

 

git cherry-pick

cherry-pick을 일부 커밋만 다른 브랜치로 골라서 복사하는 것이다.

feature 브랜치의 커밋 D, E, F 중에 E만 골라서 cherry-pick하면 main에 E` 커밋이 생긴다.

내용(diff)는 같지만 부모가 C로 다르므로 E커밋의 SHA가 E`로 달라진다.

feature이 원본 커밋은 그대로 남기 때문에, 원본은 건드리지 않고 복사만 하는 셈이다.

 

 

main 브랜치에 바로 커밋/푸시한 변경 사항을 feature 브랜치로 옮기고 PR 생성하기

간단한 작업일 줄 알고 main 브랜치에 직접 작업했는데, 생각보다 커밋 개수가 많아졌고 다른 사람들의 commit으로 인해 커밋 히스토리가 복잡해졌다.

현재 작업하던 변경 사항을 모두 revert하고 변경 사항을 새로운 브랜치로 옮긴 후 PR을 생성하고 싶었다.

내가 생각한 방법은

 

1) main에서 feature 브랜치를 생성한다.

2) main에서 feature에만 남길 변경 사항 커밋들을 리버트 한다.

3) feature에서 main으로 PR을 생성한다.

 

아래애서 fe, er, cr은 각각 f, e, c 커밋의 revert 커밋이다

main: a - b - c - d - e - f - fr - er - cr

feature: a - b - c - d - e - f

 

그런데 이런 상태에서는 bitbucket에서 PR이 생성되지 않는다.

 

PR의 diffsms merge-base(공통 조상)부터 head까지의 커밋으로 계산을 한다.

feature 브랜치의 끝 커밋인 f는 main의 히스토리 안에 이미 들어있다.

feature는 main보다 앞선 커밋이 0개이므로 서버 입장에서 합칠 커밋이 없다.

 

만약 여기서 아래 명령어를 입력하면 어떻게 될까?

git checkout feature
git rebase main -- Main 브랜치에 feature 브랜치를 rebase

이렇게하면 feature 브랜치의 변경 사항이 main에 반영되는게 아닌, feature의 헤드가 main의 마지막 커밋인 cr로 이동한다.

즉 브랜치 상태가 아래와 같이 된다.

 

main: a - b - c - d - e - f - fr - er - cr

feature: a - b - c - d - e - f - fr - er - cr

 

해결 방법 1: revert의 revert

첫번째 해결 방법은 feature와 main 브랜치의 끝을 맞춰 놓은 상태에서, feature 브랜치의 리버트 커밋들을 다시 리버트(=reapply) 하는 것이다.

crr, err, frr은 각각 cr, er, fr의 리버트 커밋(=c, e, f의 reapply 커밋)이라고 하자.

feature: a - b - c - d - e - f - fr - er - cr - crr - err- frr

 

이 상태에서는 feature -> main rebase와 PR 생성이 가능해진다.

 

해결 방법 2: cherry-pick으로 새로운 커밋을 만들기

 

main: a - b - c - d - e - f - fr - er - cr

feature: a - b - c - d - e - f 

이 상황에서

git checkout feature
git reset --hard main                 # main 끝(revert 포함)으로 이동

# 오래된 커밋부터 순서대로 3개를 새 SHA로 복사
git cherry-pick c e f

# 연속된 커밋이라면 범위로도 가능 (시작 커밋을 포함하려면 ^ 필요)
git cherry-pick c^..f

 

그러면 

feature: a - b - c - d - e - f - fr - er - cr - g

 

이렇게 g라는 새로운 커밋이 생성된다.

g에는 c, e, f의 변경 사항이 들어있다.

새로운 SHA를 가진 커밋이 만들어졌으므로, rebase와 PR 생성이 가능해진다.