[Git] 큰 작업을 작은 PR 로 나눠서 작업하기

HYUNGU, KANG·2023년 7월 11일
3

Git-CheatSheet

목록 보기
5/6
post-thumbnail

2023.10.27 Update

rebase --onto 를 사용하면 꽤나 쉽게 실제 작업 커밋들만 리베이스를 할 수 있다.
하지만 Stacked branch 갯수가 증가할수록, 체크아웃과 리베이스를 하는게 번거로워진다.

이를 해결하기 위한 rebase --update-refs 라는 옵션이 있다고 하니 살펴보길 바란다.
가장 마지막 branch 에서 리베이스를 하면, 부모를 타고 올라가면서 중간의 브랜치들을 알아서 업데이트 해준다.
https://andrewlock.net/working-with-stacked-branches-in-git-is-easier-with-update-refs/


2023.10.26 Update

이미 stacked diffs 라는 방법론이 있었다. 빅테크들에서는 이미 이러한 방식으로 일을 하고 있다고 한다.
내용은 아래에 기재된것과 큰 틀에서는 차이가 없으니 관심 있으신분들은 참고하시길 바란다.

https://newsletter.pragmaticengineer.com/p/stacked-diffs
https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/

그리고 이러한 방식으로 일을 할때는 브랜치(PR)간의 관계를 파악하거나 선행 브랜치(PR)가 머지되면
후행 브랜치(PR)들을 rebase 하는것도 일인데, 이 모두를 도와주는 graphite 라는 앱을 최근에 발견해서 공유한다.
https://graphite.dev


큰 작업을 위한 여러개의 브랜치

하나의 큰 피쳐를 여러개의 브랜치로 잘게 쪼개서 작업하면 PR 리뷰를 하는데 수월하다.
리뷰를 하는 입장에선 검토해야 할 변경사항이 확연하게 줄어들고, 작은 단일 맥락에서 리뷰하기만 하면 된다.
리뷰를 제출하는 사람 입장에서도 어떤 변경인지에 대해서 맥락을 전달하기가 쉽다.

main
feat/feature-name/base
feat/feature-name/job-1
feat/feature-name/job-2
feat/feature-name/job-3

이렇게 작업된 브랜치들은 다음과 같은 방향으로 리뷰 후 머지가 된다.

feat/feature-name/basefeat/feature-name/job-1
feat/feature-name/basefeat/feature-name/job-2
feat/feature-name/basefeat/feature-name/job-3
...
main <- feat/feature-name/base


다음 작업을 진행하기 위한 브랜치

바로바로 리뷰가 되고 다음 작업을 시작할 수 있다면 굉장히 이상적이겠지만, PR 을 생성하고 리뷰를 받고 수정까지는 꽤 시간이 소요된다.
그렇다고 아무것도 안하고 리뷰만을 무턱대고 기다릴수도 없는 노릇이다.

브랜치 목록
main
mainfeat/feature-name/base
mainfeat/feature-name/basefeat/feature-name/job-1

Pull Request
feat/feature-name/basefeat/feature-name/job-1

그래서 다음 작업을 시작하려면 feat/feature-name/base 브랜치에서 새롭게 feat/feature-name/job-2 을 만들어서 작업을 하자니 문제가 생긴다.

하나의 큰 피쳐를 여러개로 잘게 쪼개서 작업을 하다보면 필연적으로 선행해서 작성한 코드가 후행 작업에도 필요한 상황이 발생하기 때문이다. (혹은 컨플릭트를 방지하기 위해서) 이 경우에 후행 작업을 위해서, PR 리뷰를 기다리고 있는 선행 브랜치에서 새롭게 브랜치를 딸 수밖에 없게된다.

브랜치 목록
main
mainfeat/feature-name/base
mainfeat/feature-name/basefeat/feature-name/job-1
mainfeat/feature-name/basefeat/feature-name/job-1feat/feature-name/job-2

Pull Request
feat/feature-name/basefeat/feature-name/job-1

브랜치의 커밋 기록이 남는식으로 머지를 한다면 문제가 되지 않는다. 그냥 Rebase 를 해도 별도 처리없이 잘 된다.
하지만 여러개로 잘게 쪼개서 작업하는 경우, 커밋 히스토리나 브랜치 그래프를 깔끔하게 관리하기 위해서 혹은 레포지토리 정책에 의해서 Squash merge 를 하게 될 가능성이 존재한다.

그렇다면 여기서 job-2 에서 작업을 진행하던 도중 job-1 이 squash merge 가 되면 어떻게 될까? 커밋을 살펴보도록 하자.

선행 브랜치가 Squash merge 됐을때 conflict 없이 rebase

스쿼지 머지 전
feat/feature-name/base
feat/feature-name/job-1 - Commit A - Commit B
feat/feature-name/job-2 - Commit A - Commit B - Commit C - Commit D - Commit E

스쿼시 머지 후
feat/feature-name/base - Commit JOB-1
feat/feature-name/job-2 - Commit A - Commit B - Commit C - Commit D - Commit E

알다시피, squash merge 를 하면 모든 커밋이 하나로 합쳐 머지가 된다.
위에서는 Commit ACommit B 가 합쳐져서 Commit JOB-1 이 되었다.

이제 job-2 브랜치를 base 에 머지할 때 머지가 잘 되면 좋겠지만... JOB-1 은 같은 변경사항을 가졌지만, 다른 커밋이기 때문에 컨플릭트가 발생한다. (물론 리베이스 또한 컨플릭트가 발생한다.) 그러면 컨플릭트 없이 어떻게 정리를 할 수 있을까?

우리는 job-1 브랜치로부터 온 Commit A, Commit B 를 제외한 Commit C, Commit D, Commit E 만을 base 에 리베이스 하면 된다. 결국 job-2 는 아래와 같은 그림이 되어야 할것이다.

브랜치 목록
main
mainfeat/feature-name/base
mainfeat/feature-name/basefeat/feature-name/job-2

Pull Request
feat/feature-name/basefeat/feature-name/job-2

커밋 히스토리
feat/feature-name/base - Commit JOB-1
feat/feature-name/job-2 - Commit C - Commit D - Commit E

이렇게 리베이스를 해주기 위해서는 리베이스에 --onto 옵션을 주면 된다.

# 현재 브랜치를 feat/feature-name/base 로 리베이스 하는데, HEAD에서부터 3개까지의 커밋만 리베이스 하겠다는 의미
git rebase --onto feat/feature-name/base HEAD~3

만약 job-2 브랜치를 삭제하지 않았다면 아래와 같이 명령어를 사용할수도 있다.

# job-1 과 job-2 의 base(공통 조상) 까지의 커밋(Commit A, Commit B)을 제거해서 리베이스 하겠다는 의미
git rebase --onto feat/feature-name/base feat/feature-name/job-1 feat/feature-name/job-2

이렇게 작업을 진행하면 리뷰를 기다리지 않고, conflict 에 대한 걱정 없이 연속적으로 작업을 진행할 수 있다.

References

profile
JavaScript, TypeScript and React-Native

0개의 댓글