본문 바로가기
프로그래밍/git

[git] merge, squash & merge 그리고 rebase의 원리에 대해서 알아보자

by 사바라다 2021. 10. 2.

안녕하세요. 오늘은 merge와 squash & merge 그리고 rebase의 차이점에 대해서 알아보는 시간을 가지도록 하겠습니다. 오늘은 이론적인 부분에 대해서만 말씀드릴 것이며 명령어를 실습하는 것은 다음시간에 이어서 진행하도록 하겠습니다.

branch 병합

개발은 혼자 진행할 수도 있지만 대개는 여러명이 팀을 이루어 함께 개발을 합니다. 이럴 경우 github, gitlab과 같은 원격 저장소를 이용하여 코드의 형상관리를 하게됩니다. 이때 branch라는 개념을 이용하여 각자가 맡은 기능에 대한 개발을 진행하고 개발이 완료되면 master와 같은 base 브런치에 병합(merge)한 후 하나가 된 base의 코드를 서버에 배포하는 과정을 거치게됩니다.

예제 git flow

위 이미지는 1개의 base 브런치에서 2개의 기능개발(feature) 브런치가 생성된 것을 나타내고 있습니다. 동그라미는 commit이며 가로축은 commit이 된 시간이라고 보시면 됩니다. 보시면 2개의 기능개발 브런치에서 커밋 시간 순서가 뒤죽박죽인것을 확인하실 수 있습니다. 그렇다면 이런 경우 2개의 기능개발 브런치를 base 브런치로 어떻게 병합하면 좋을까요 ?

기능 개발을 완료한 branch를 병합하는 방법은 3가지가 있습니다. 그것이 바로 오늘 이야기할 merge, squash & merge 그리고 rebase 입니다. 그리고 여러 사람이 개발을 진행하다보면 동일한 파일을 수정할 때가 있습니다. 이 경우 충돌(conflict)이 발생하게 되는데요. 이때 정상적으로 병합하기 위해서는 충돌 해결이 필요하게 됩니다. 각 병합 방법에 따라 충돌 해결하는 방법도 다릅니다. 충돌은 어떻게 해결하는지도 함께 확인해보도록 합시다.

merge

merge는 기본적인 git의 가장 기본적인 병합방법입니다. merge는 2개의 커밋이 있을 때 모든 커밋이 결과적으로 시간 순서대로 병합이 되며 충돌이 있다면 해당 충돌을 해결하는 커밋이 하나더 추가되어지게 됩니다.

예제의 이미지를 통해 한번 더 확인해보도록 하겠습니다. 예제는 branch 병합에서 보았던 이미지입니다. 해당 이미지를 merge를 통해 병합하도록 하겠습니다.

위 이미지는 위쪽으로 뻣어나갔던 branch만 병합했을때 나오는 git log 입니다. 보시면 그대로 base 브런치의 미자막이었던 A4에 붙는것을 확인할 수 있습니다.

위 이미지를 보도록 하겠습니다. 위 이미지는 아래의 branch도 병합을 했을 때 나타나는 git log입니다. C의 커밋들이 B 커밋들 사이사이에 들어가 있는것을 확인하실 수 있습니다. 이 이유는 가로축이 시간이라고 말씀드렸었는데 Commtit 시간이 이 순서였기 때문입니다. 그리고 마지막으로 MC라고 Merge Commit이 있습니다. 이것은 병합에 충돌이 있을 대 뒤에 Commit을 추가하여 해결한다는 뜻입니다.

이미지를 보시면 아시겠지만 이럴경우 commit을 되돌리는 revert를 하거나 git log를 확인할 때 한번에 눈에 들어오지 않는 단점을 가집니다.

squash & merge

squash & merge는 병합을 할 때 여러개의 커밋을 하나의 커밋으로 합친 후 merge하는 방식을 말합니다.

위 이미지는 위쪽의 branch를 병합할 때 일어나는 과정입니다. 위 이미지를 보시면 먼저 해당 커밋들을 합치는 squash 과정이 먼저 발생하게 됩니다. 그리고 이렇게 합친 Commit을 base 브런치에 붙이게 됩니다. 이렇게 하면 base 브런치에는 하나만의 커밋이 추가되게 됩니다.

그리고 두번째로 아래의 branch를 합쳐보도록하겠습니다. 아래의 브런치를 합칠 때도 마찬가지로 sqash로 하나의 커밋으로 합치는 것을 확인할 수 있습니다. 그런데 여기서 보시면 충돌이 있기 때문에 먼저 Merge Commit을 하나 추가해서 충돌을 제거해준 후 sqash를 진행합니다. 이렇게 한 후 base 브런치에 합치면 충돌이 일어나지 않고 자연스럽게 병합이 이루어지며 1개의 커밋만이 추가로 이루어집니다.

결과적으로 총 2개의 커밋이 들어가며 일반 커밋과는 다르게 Merge Commit이 base 브런치에 들어가지 않습니다.

rebase

마지막으로 rebase는 커밋의 시간에 관계없이 마지막에 merge 되는 branch의 commit을 가장 뒤에 붙이는 전략입니다.

먼저 위의 이미지는 위쪽 branch를 rebase 병합했을 때입니다. 사실 일반 merge와 크게 다르지 않다라는 것을 알 수 있습니다. 바로 이어서 아래 branch를 rebase 병합할 때를 보도록 하겠습니다.

위 이미지가 아래 branch를 병합했을 때의 결과입니다. 보시면 branch log가 B4 뒤에 이어서 붙은것을 확인하실 수 있습니다. 그리고 MC와 같은 충돌을 제거하는 commit 이 없다는 것을 확인하실 수 있습니다. 어떻게 이렇게 할 수 있는걸까요 ? 잘 보시면 C1 ~ C4가 붙은것이 아니라 C'1 ~ C'4가 B4뒤에 붙었다는 사실을 알 수 있습니다. 여기에 힌트가 있습니다. rebase의 구체적인 flow는 아래와 같습니다.

  1. C1 ~ C4와 B1의 충돌을 제거합니다.
  2. B1과의 충돌을 제거한 C1 ~ C4와 B2의 충돌을 제거합니다.
  3. B2과의 충돌을 제거한 C1 ~ C4와 B3의 충돌을 제거합니다.
  4. B3과의 충돌을 제거한 C1 ~ C4와 B4의 충돌을 제거합니다.
  5. B4와의 충돌을 제거한 C1 ~ C4를 B4 뒤에 붙입니다.

이런 과정을 통해서 Merge Commit 없이 병합을 할 수 있는 것입니다. 이때 충돌제거 하는 과정이 1번이 아니며 최대 commit 수 만큼 나올 수 있습니다. 그래서 C1 ~ C4는 사실 머지가 되며 코드가 변경되게 됩니다. 그렇기 때문에 commit hash도 변경되게된 결과 C1 ~ C4가 C'1 ~ C'4로 변경되게 되는것입니다. 또한 이렇게 변경이 되었기 때문에 일반적인 push로는 원격저장소에 넣을 수 없습니다. 그래서 force push가 필수가 됩니다.

rebase는 force push를 해야하기 때문에 사용하실 때 조심히 사용하시기 바랍니다.

마무리

각 병합방법에 따라 장점과 단점을 가지고 있습니다.

본인 회사의 정책에 따라서 적절하게 사용하시면 좋을것 같습니다.

감사합니다.

댓글