sungyup's.

dev_toolkits / Git / 2.1 깃 병합 전략들

2.1깃 병합 전략들

merge, rebase와 cherry-pick 비교

TL;DR

추억의 쪽지 시험

새 회사의 프로젝트에 온보딩하며, 지금까지 해오던 것과 다른 깃 컨벤션을 배울 기회가 생겼다. 또, 기존과는 다른 브랜치 전략에서 협업하며 처음으로 써본 깃 병합 전략(cherry-pick)도 있었다. 이번 기회에 서로 다른 깃 병합 전략들과 옵션들을 정리해본다.

1. merge, rebase와 cherry-pick

이 세 개는 모두 깃 브랜치(또는 커밋)를 합치는 방식들인데, 모두 그 동작법이 다르고 그에 따라 목적이 다르다.

우선 가장 많이 써본 merge는 두 개의 브랜치가 있을 때 두 브랜치의 끝을 합쳐서 새로운 merge commit을 생성하는 방식이다.

예를 들어, main 브랜치가 있고 이 브랜치의 코드가 상용 배포된 상태라면 개발 시엔 develop 브랜치로 분기(git checkout -b develop)해서 개발 후, main 브랜치로 돌아와 develop 브랜치를 병합한다.

bash
git merge develop
3 way merge
이미지 출처 : #
merge는 새 브랜치를 base가 될 브랜치에 합하는 commit을 생성하여 base 브랜치에 병합하는 방식으로, 이 같은 방식을 3-way merge라고도 부른다.

이 방식은 기본적으로는 히스토리를 있는 그대로 보존한다. 누가 언제 다른 브랜치에서 작업해서 언제 합쳤는지 추적이 가능하다.

merge는 가끔 별도의 merge commit 없이, 즉 3-way merge가 아닌 방식으로도 합칠 수 있다. 만약 새 브랜치와 base 브랜치가 있었는데, base 브랜치엔 커밋이 딱히 없었고 새 브랜치에만 커밋이 있었다면(즉, 공통 조상과 현재 브랜치 사이에 차이가 없어서, 포인터만 이동하면 되는 경우) 그냥 새 브랜치의 맨 마지막 커밋을 그냥 base 브랜치의 맨 마지막 상태로 보면 되는 것이다. 이걸 fast-forward라고 부른다.

fast forward merge
이미지 출처 : #
굳이 머지 커밋을 만들 필요가 없는 이런 경우엔 그저 신규 브랜치를 base 브랜치라고 하면 된다

다만 병합 히스토리를 남기는 것이 동료들이 추적하거나 기록을 파악하는데 더 용이한 경우도 있다. 이런 경우엔 --no-ff 플래그로 fast forward를 막는 옵션을 설정할 수 있다. 참고로, 앞서 굳이 --ff 플래그를 달지 않았는데도 fast forward가 된 것은 이게 기본 설정이기 때문이다.

bash
git merge develop --no-ff

이렇게 merge하고 브랜치를 삭제하고 싶으면 git branch -d <브랜치명>을 실행한다. 만약 merge되지 않은 브랜치지만 삭제하고 싶다면 -d 대신 -D를 쓰면 된다.

--squash를 쓰면 다른 브랜치의 많은 커밋을 하나의 커밋으로 합쳐서 반영할 수도 있다. 이때는 실제 merge가 되진 않고 단일 커밋으로 준비가 되어, git commit으로 반영을 해줘야 한다.

bash
git merge develop --squash

rebase는 깃을 병합하면서도 커밋들이 별도 브랜치에서 작업한게 아닌, 마치 base 브랜치에서 작업했던 것처럼 커밋을 다시 적용해 히스토리를 재작성하는 방식이다. 이를 위해서 우선 별도 브랜치로 checkout 한다음, 합쳐지고자 하는 base 브랜치에 rebase한다.

bash
git checkout develop git rebase main
merge와 rebase의 차이
이미지 출처 : #
별도의 브랜치가 base 브랜치에 합쳐지는 merge와 마치 처음부터 base 브랜치였던척 하는 rebase

왜 굳이 merge를 두고 rebase로 합칠까? 이는 커밋들을 봤을때 이력 파악이 보다 쉽기 때문이다. 브랜치가 나뉘었다 합쳤다가 또 나뉘었다 합쳤다가...하는 것을 보다보면 정작 중요한 코드 변경 내역을 확인할 시간을 뺏긴다. rebase는 하나의 브랜치에서 커밋 메시지와 코드의 변화를 보다 깔끔하게 파악하게 해준다.

즉, rebase의 목적은 히스토리 정리다.

cherry-pick커밋 몇 개만 발췌해서 옮긴다. 이번에 나는 회사에서 팀원들이 장기 프로젝트인 언어 번역 작업을 하는 도중에 상용 웹페이지의 버그를 수정했어야 했다. 언어 번역 작업이 상당히 진행된 와중에, 상용에 버그를 수정하고 나니 해당 커밋들을 언어 번역 작업이 진행중인 브랜치에도 옮겨야 했다.

이 때 방법을 찾다가 알게된 것이 cherry-pick이다. git log로 옮겨야하는 커밋 해시들을 파악하고, 해당 브랜치에서 필요한 커밋들을 cherry-pick 해왔다.

bash
git checkout i18n git cherry-pick <hash1> <hash2> <hash3> ...
NextNo next post