0. 들어가며 🏃🏻♂️
프로젝트를 혼자서 진행할 때는 대부분 main 브랜치에서 작업하거나 간단하게 브랜치를 파서 작성한 후 별 생각 없이 merge 하는 것을 반복하곤 했습니다.
최근 경험한 협업 프로젝트에서는 각 팀원들이 feature 브랜치를 통해 기능 개발을 한 뒤 main 브랜치에 merge하는 과정을 거쳐야 했습니다.
git에서는 여러 가지 merge 전략이 있으며, 전략에 따라 커밋 히스토리가 크게 달라집니다. 이에 따라 저희 팀 상황에 맞는 merge 전략이 무엇일지 알아보는 시간을 가져보았습니다. 이번 글에서는 git merge 전략에 대해 정리해보도록 하겠습니다.
1. Merge 🍎
두 브랜치를 합치는 가장 기본적인 방법으로 일반적인 Merge 명령어가 있습니다. 상황 설명을 위해 아래 그림을 살펴보도록 하겠습니다.
위 같은 상황은 base에서 branch1이 분기되어 C2, C3의 commit이 생성되었고, main branch에서는 C4의 commit이 존재하는 상황입니다. 이 상태에서 main 브랜치가 branch1을 아래와 같은 명령어로 merge하면 어떻게 될까요?
1. git checkout main
2. git merge branch1
merge는 마지막 커밋 두개인 C3, C4와 base 커밋(공통조상)인 C1을 가지고 3-way merge를 수행하게 됩니다. 즉, C1, C3, C4를 비교하여 새로운 커밋을 만들고 병합을 수행하게 됩니다. 따라서 그 결과로 아래와 같은 커밋 히스토리를 남기게 되죠.
(3-way merge에 대한 내용은 정리되어있는 블로그가 많아 자세한 설명은 생략하도록 하겠습니다.)
위 커밋 히스토리를 보면 Commit_2, 3, 4이 커밋된 순서에 따라 나타나있는 것을 알 수 있으며, 마지막에 병합한 커밋(Commit_5)가 생성된 것을 확인할 수 있습니다. 단순 merge의 경우 커밋한 모든 히스토리가 남으며, 마지막에 병합한 기록까지 모두 나타냄을 알 수 있습니다.
2. Squash and Merge 🍊
feature 브랜치에서 기능을 개발하다보면 지저분한 Commit 기록을 남기게 될 수도 있습니다. 예를 들어 개발하다보니 branch1의 커밋이 위 상황과 다르게 많은 커밋을 남기게 되었고, 이러한 커밋들이 굳이 히스토리에 남지 않아도 될 것 같은, 즉 최종적으로 개발한 결과물만을 main 브랜치에 히스토리로 남기고 싶을 수 있습니다. 이럴때 사용할 수 있는 merge 전략이 바로 Squash and Merge입니다. Squash Merge의 경우 아래와 같은 코드로 병합할 수 있습니다.
1. git checkout main
2. git merge --squash branch1
위와 같은 코드로 merge를 하면 아래 그림과 같은 모습이 됩니다!
그림을 살펴보면 C2와 C3 커밋이 Squash 되어 새로운 커밋 C5를 만들고 이를 main 브랜치에 병합한 모습을 확인할 수 있습니다. 이렇게 병합을 수행하게 되면 커밋 히스토리가 아래와 같이 남게 됩니다.
일반 merge와 다르게 커밋 히스토리가 간단해진 모습을 볼 수 있죠. 이를 통해 조금 더 깔끔한 커밋 히스토리를 만들어낼 수 있습니다. 하지만 Squash and Merge의 경우 C2, C3 커밋 각각에 대해 되돌리기 어렵습니다.
3. Rebase and Merge 🌲
Rebase and Merge 전략의 경우 Squash and Merge와 다르게 모든 Commit을 히스토리에 남길 수 있고 Merge했다는 히스토리를 남기지 않을 수 있습니다. 이를 통해 마치 하나의 브랜치에서 작업한 것과 같은 커밋 히스토리를 만들어 낼 수 있죠.
Rebase and Merge는 아래와 같은 코드로 수행할 수 있습니다.
1. git checkout branch1
2. git rebase main
3. git checkout main
4. git merge branch1
위 코드 중 4번째 줄의 경우 main을 branch1이 가리키고 있는 곳으로 fast-forward 하는 과정입니다. 위 코드를 입력했을 시 일어나는 일을 아래 그림을 통해 살펴보도록 하겠습니다.
위 그림을 살펴보면 분기되었던 branch1의 커밋들이 마치 애초에 main에서 작업된 것처럼 보입니다. 실제 커밋 히스토리의 경우도 아래와 같이 나타나는데요.
main 브랜치의 커밋 히스토리를 보면 마치 분기된 적이 없던 것처럼 모든 커밋이 예쁘게 나타나있고 merge했다는 커밋도 없는 것을 알 수 있습니다. 다만 Rebase and Merge의 경우 각 커밋마다 conflict를 해결해주어야 하기 때문에 위에 소개했던 다른 merge 방식보다는 다루기가 조금 더 까다롭다는 단점이 있습니다.
4. 그래서..📚
이번 협업 프로젝트에서는 Squash and Merge 전략을 선택하기로 하였습니다. 각자 feature 브랜치에서 작업한 내용을 머지한 후 feature 브랜치를 삭제하는 상황이 많을 것이라 예상했고, 굳이 main 브랜치에 지저분한 커밋 히스토리를 남기지 않는 것이 좋다고 판단했습니다. 또한 각 팀원들이 git에 아주 능숙한 상황은 아니여서 Rebase and Merge 방식에서 발생할 conflict 해결 과정이 부담스럽게 작용할 수 있을 것이라고 생각했습니다.
5. 나가며 💨
이번 글에서는 git의 세 가지 merge 전략에 대해 알아보았습니다. 혼자 코드 작업을 할 때는 커밋 히스토리를 그렇게 신경쓰지 않았었는데 큰 프로젝트를 다루거나 협업 할 일이 있을 경우 커밋 히스토리를 잘 관리하기 위해 merge 전략에 대해 잘 알고 있는 것이 중요하다는 사실을 알게된 계기가 된 것 같습니다.😀
6. 레퍼런스 📖
https://im-developer.tistory.com/182
https://meetup.toast.com/posts/122
https://velog.io/@godori/Git-Rebase
https://inmoonlight.github.io/2021/07/11/Git-merge-strategy/