"우리 팀은 merge 전략을 rebase로 하자" 라는 말과 함께 프로젝트를 진행하였다.
하지만 rebase merge를 하려는 중 얼마 못가 conflict를 마주쳤고 정확한 원인을 알지 못했다.
merge에 대해 정확하게 알지 못하고 있다고 깨닫고이번 기회에 merge에 대해 깊게 공부를 해보려 한다.
이 글을 통해 다른 개발자들도 merge 개념의 이해에 도움이 되면 좋겠다.
브랜치 간에 변경 사항을 병합하는 작업이다.
Merge에 방식에는 크게
1-1. Fast forward merge
1-2. Recursive merge
2. Squash and merge
3. Rebase and merge
가 있다.
깃허브에서 merge하는 방식을 보면 3가지 방식이 있는것 또한 확인할 수 있고
각각의 Merge의 특징을 살펴보며 차이점을 살펴보자.
가장 기본적인 Merge 방식이다. main 브랜치에서 dev 브랜치로 분기 후 dev 브랜치에서 작업을 하다 이 작업 결과를 main 브랜치에 그대로 옮기는 방식이다.
dev의 B와 C 커밋 그 자체가 main에 복사 된다고 생각하면 된다. (커밋의 해쉬값 까지 동일하다)
이 Fast forward merge를 할 수 있는 경우는 dev가 main에서 분기 후 main에서는 어떠한 작업도 하지 않을 경우이다.
가장 많이 사용하는 일반적인 Merge 이다.
개발을 진행해보며 Fast forward merge 처럼 main에서 어떠한 작업도 이루어 지지 않는 경우는 거의 없었다.
Merge 하기 전 팀원들의 작업물 들이 main에 먼저 Commit된 경우가 대다수이기 때문이다.
이처럼 A Commit 에서 분기된 dev 브랜치가 B,C 작업을, A Commit 이후 main 브랜치에서 X,Y작업을 진행한 상황에서 dev 브랜치의 작업을 main에 Merge를 해야할 때가 있다.
이 때에는 Git 입장에서 Y Commit과 C Commit 중 어떠한 것이 먼저 일어난 것인지 알 수 없고 따라서 HEAD를 어디에 이동 시켜야 할지 모른다.
따라서 새로운 Z Commit 을 생성하고 dev 브랜치 작업물을 main에 merge 한다.
그럼 main에는 B,C,Z 3개의 Commit 내역이 추가된다.
이것이 Recursive merge(Commit 후 merge를 하기에 commit merge라고 많이 부르는것 같다)이다.
main에서 작업했던 것(X,Y)와 dev에 작업했던 것(B,C)이 충돌이 일어나곤 한다.
같은 파일을 수정하는 경우가 있기 때문이다.
이때에는 어떤 코드를 사용할 지는 사람이 결정해서 코드를 수정 후 Z와 같이 Commit을 진행한다.
이것은 Resolve conflict 라고 한다.
개발을 하면서 한번도 사용한 적이 없는 Merge 방식이다.
공부를 하면서 이 merge 방식도 상당히 매력이 있다고 느꼈다.
main branch commit 내역에 dev의 commit이 합쳐져 하나의 Commit으로 깔끔하게 보이기 때문이다.
위와 같이 dev에서 main 으로 merge를 하기 전 B,C가 합쳐진 새로운 D Commit을 만들고 main으로 merge 된다.
이때 main 브랜치는 B,C 커밋은 존재하지 않고 D만 존재하게 되어 깔끔한 Commit 이력을 유지할 수 있다.
하지만 다른 브랜치에서 열심히 작성한 커밋 이력이 모두 없어지기에 잔디가 심어지지 않는다 ㅠㅠ
마지막으로 rebase merge이다. 이 게시글을 쓰게된 merge의 장본인이라고 할 수 있다.
이 merge 방식은 1-1의 Fast-forward merge 방식으로 볼 수 있는데 다른 점은 main에 어떠한 작업이 존재해도 merge를 할 수 있다는 것이다.
또한, Fast-forward merge 일 경우에 dev 브랜치의 Commit들이 그대로(해시값 유지) main 브랜치로 복사가 됐지만, rebase의 경우 커밋내용은 동일하지만 새로운 커밋들(해시값이 다름)이 main브랜치에 복사된다고 할 수 있다.
merge commit을 하지 않기 때문에 main 브랜치 하나로 작업한 것 처럼 보여 매우 깔끔한 commit내용을 유지할 수 있다.
그럼 1-1의 Fast-forward와 다르게 Rebase merge의 경우 dev 분기 후 main에 작업이 존재할 수 있고 이것은 dev와 충돌이 발생할 수 있다.
이때 dev와 main의 충돌 난 부분을 하나하나 해결하고 rebase를 다시 진행하면 해결할 수 있다.
하지만 나의 경우 위의 방식처럼 dev로 main을 먼저 Recursive merge을 진행한 뒤 다시 main에 Rebase merge를 진행하려고 하여 D의 Commit은 main의 부모를 가지고 이 브랜치 때문에 꼬여서 Rebase merge를 진행하지 못하였다.
이 경우에는 다시 main 브랜치로 Commit Merge를 진행할 수 밖에 없다.
결론적으로 Rebase는 main 브랜치와 dev 브랜치가 충돌이 나지 않을 경우 merge를 진행할 수 있고 만약 충돌이 난다면 dev 브랜치에서 충돌난 부분을 하나하나 풀어 Rebase를 진행해야한다.
main의 브랜치를 dev로 merge 후 다시 main으로 merge를 진행하려고 하면 Rebase를 쓸 수 없고 commit Merge를 진행하자.
참고 : https://mangchhe.github.io/git/2021/09/04/GitMerge/
https://kotlinworld.com/277
https://goddaehee.tistory.com/253
https://wonyong-jang.github.io/git/2021/02/05/Github-Rebase.html
https://backlog.com/git-tutorial/kr/stepup/stepup2_8.html
This article is truly astounding. Appreciative for sharing. https://www.aimproviderportal.org/