ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Github] Branch 병합(Merge, Squash, Rebase)
    Environment(개발환경)/Git & Github 2022. 2. 18. 13:26
    반응형

    오랜만에 프론트앤드 개발 혹은 알고리즘 외의 주제로 포스팅을 작성하는 것 같다!!!

     

    이 글을 포스팅하게 된 계기는, 개인 및 회사 프로젝트들을 진행하면서 Github를 사용하다보니 merge 외에 다른 병합 전략들을 접하게 되어 이를 정확히 알아보고 정리하고자 작성하게 되었다.

     

    다수의 개발자들이 프로젝트를 진행하면, 개인의 코드를 관리하는 Git, 이 코드들이 병합되는 원격 저장소 Github(혹은 Gitlab 등) 과 같은 저장소를 사용하게 된다.

    이런 Git, Github 와 같은 협업환경에서는, branch(브랜치)가 구성되어있어 개인환경에서 작업하거나 이를 main(master)과 같은 공용환경에 합치는 등의 작업이 가능하다. (Git & Github 정리 포스팅)

     

    [Git & Github] Git 다시 공부하기 (드림코딩 엘리)

    🙀 다시 공부해보는 Git 과 Github! Git 명령어를 통해 직접 Github로 push까지 해보았지만, 아직 사용하는데 있어 익숙치가 않았다. 또한, branch나 commit 등 갈 길이 멀기에, 심화를 들어가기 전 드림코

    abangpa1ace.tistory.com

     

    이렇게 개인의 코드(작업물)를 특정 브랜치로 수렴시키는 작업을 병합(Merge)이라고 하는 것이다.

    이번 포스팅은, Github에서 할 수 있는 3가지 병합방법인 Merge, Squash & Merge, Rebase & Merge 에 대해 정리해보고자 한다!


    📕 Branch 병합

    특히 회사에서 프로젝트를 진행하면, 여러 명의 개발자가 팀을 이루어 함께 개발을 진행하는 경우가 많다.

    이 때, Github, Gitlab 등과 같은 원격 저장소를 이용해 소스코드를 공유하고 작업물을 통합하는 등의 형상관리를 하게 된다.

     

    여기서 Branch라는 개념을 통해 각자의 작업물을 개발하고, 개발이 완료되면 base 브랜치(통상 main or master)에 본인의 작업물을 더하는데 이 과정을 병합(Merge)이라고 하는 것이다.

     

    git flow

    위 그림은 main(A) 브랜치에서 B, C 브랜치를 분기해서 작업한 뒤, 다시 main으로 병합(Merge)하는 과정을 간단히 묘사했다.

    이러한 패턴으로, 개발자들은 각자의 브랜치에서 기능개발을 진행한 뒤, main 브랜치에 이를 첨부하여 다음 배포를 준비한다.

     

    병합(Merge)의 3가지 방법이 있는데, 이것이 포스팅 주제인 merge, squash(& merge), rebase(& merge) 이다.

    병합방법에 따라 commit log(작업기록) 히스토리가 달라지고, 코드가 충돌하는 conflict의 해결방법도 달라진다.

     

    - Commit History

    위 3가지 병합방법이 보이는 큰 차이점은 바로 Commit History(커밋 히스토리) 이다.

    개발자들이 작업을 일정량 진행한 뒤, 이를 저장하는' git add' 와 작업기록은 남기는 'git commit -m [메세지]' 를 진행한다.

     

    이렇게 남긴 커밋들의 이력을 커밋 히스토리라고 하며, 이 커밋 히스토리가 명확하게 관리될수록

    어느 시점에 어떤 작업이 진행되었는지, 그리고 특정 버전에서 버그가 발생했을 때 이전 버전으로 돌아가는 백업 등의 작업이 수월해진다.

     

    이를 명심하고, 곧 알아볼 3가지 병합 방법들의 커밋 히스토리는 어떻게 다른지, 그리고 커밋간의 충돌(conflict)이 어떻게 해결되는지 정리해보았다!


    📕 Merge의 3가지 방법

     

    1. Merge

    git checkout main
    git merge my-branch

    기본적인 git에서의 병합 방법이다. main 브랜치와 feature 브랜치의 모든 커밋 히스토리를 병합하는 방식이다.

    Merge Commit에서 역시간순으로 돌아가며 부모를 찾고 이를 통해 브랜치가 형성된다. Merge Commit은 C, Init 2개를 부모로 가진다.

     

     

    * Conflict(충돌) 다이어그램

    병합 전

     

    병합 후

     

    main 브랜치와 feature 브랜치 모두 수정이 있다고 가정하자.

    둘의 커밋 히스토리가 시간순으로 병합되다보면 일치하지 않는 부분이 발생할 것이다. 여기서 conflict 들이 발생할 수 있다.

    (B1~C1, B2~C2 등등)

     

    이렇게 충돌이 발생하는 지점에서 새로운 Conflict Commit이 생성되며, 이를 해결해야 최종적으로 병합이 마무리된다.

     

     

     

    2. Squash & Merge

    $ git checkout main
    $ git merge --squash my-branch
    $ git commit -m "commit message"

    feature 브랜치의 모든 커밋 히스토리를 합쳐 하나의 새로운 commit을 생성해서 main 브랜치에 추가한다.

    squash된 커밋(A, B, C)는 최초 출발점인 Init 1개의 부모만을 가진다. 커밋 히스토리가 깔끔해지며, feature 로그의 중요도가 낮을 경우 유용한 전략이다.

     

     

    * Conflict(충돌) 다이어그램

    병합 전 (B는 기병합)

     

    병합 후

    그림을 보면 C브랜치를 A브랜치(main)에 병합하기 전에 로그들을 Squash 하는 작업이 선행되는 것을 알 수 있다.

    일반 Merge와 달리, Squash 시점에서 Merge Commit이 생성되며, 여기서 A4와의 충돌여부를 확인하여 해결한 뒤 Squash가 마무리된다.

    이렇게 Squash가 마무리된 하나의 커밋이 main에 병합되므로 히스토리에 Conflict(Merge Commit)이 남지 않는다는 특징이 있다.

     

     

     

    3. Rebase & Merge

    $ git checkout my-branch
    $ git rebase main
    $ git checkout main
    $ git merge my-branch

    feature에서 작업한 커밋 히스토리(A, B, C)를 유지한 채 병합하는 방법이다.

    단, feature의 부모를 Init이 아닌 최신 버전의 main 브랜치(E)로 최신화한 뒤, 커밋 히스토리를 연장한다는 차이점이 있다.

    모든 커밋은 1개의 부모를 가지므로 마치 main 브랜치에서만 작업이 된 것처럼 보여진다.

     

     

    * Conflict(충돌) 다이어그램

    병합후 (C브랜치 -> 메인)

    C브랜치를 main브랜치의 A4에서 B4로 Rebase 한 뒤 머지한 다이어그램이다. 그렇기에, 시간순서가 아닌 B4 뒤에 C브랜치의 커밋 히스토리가 붙게 된다.

     

    이 때 충돌로 인한 Merge Commit이 없는 것을 알 수 있는데, 이 부분과 C가 아닌 새로운 C' 커밋으로 갱신된 점이 Rebase의 특징이다.

    C를 Rebase 할 때, B1 -> B2 -> B3 -> B4 순으로 충돌을 제거한다.

    즉, 최대 커밋 횟수만큼 충돌이 발생할 수 있으며, 이 때마다 머지가 발생하고 이렇게 코드가 갱신된 C' 커밋으로 최종 변경되는 것이다.

     

    Local(git)에서 Rebase를 사용한 경우 일반적인 push로는 원격 저장소에 넣을 수 없다. 반드시, force push를 해야하므로 Rebase를 하기 전에 항상 유의를 해야 한다.

     

     

     

     

    📕 Merge 사용 예시

    • feature -> develop 간 : Squash & Merge 가 유용하다. feature의 커밋 히스토리를 묶어 develop에 넣을 수 있으며, develop에서 하나의 커밋으로 독자적으로 관리할 수 있기 때문에 커밋 히스토리가 간결해지고 작업물 단위로 관리되는 이점이 있다.
    • develop -> main 간 : Rebase & Merge 가 유용하다. develop의 내용을 main에 추가할 경우(통상 배포 전) 별도의 커밋을 생성할 필요가 없으며, 최신의 main을 기반하여 배포 버전으로 갱신하는 것이 유리하기 때문이다.
    • hotfix -> develop / main 간 : Merge 또는 Squash & Merge 가 유용하다. hotfix 커밋들이 필요하면 전자, 아니면 후자의 경우로 관리하는 것이 좋으며, hotfix의 경우 Merge Commit이 남아있어야 하기 때문이다.

    당연히 나는 협업간에 Merge만 사용을 했었고, 현업과정에서 다양한 병합방법을 사용하는 과정들을 겪게 되었다.

     

    Squash의 경우 이번에 새로 개발한 통합 멤버십을 Develop에 병합할 경우, 멤버십 커밋 히스토리들이 구체적으로 남을 필요가 없어서 Squash를 적용하게 된 케이스이다.

     

    Rebase의 경우 현재 회사 Github에서 모노레포를 임의로 구현했기 때문에, 각 서비스의 release 브랜치가 develop에 병합될 경우 Merge Commit을 남기지 않고 각 서비스별로 커밋 로그가 분리되는 데 이점이 있기 때문에 Rebase를 활용하고 있다.

     

    기본적인 Merge를 사용해도 충돌만 잘 해결하면 문제가 없지만, develop이나 main 과 같은 중앙 브랜치를 관리하기 위해 Squash나 Rebase와 같은 전략을 적절히 선택하는 것도 중요하다고 느꼈다.

     

     

    📎 출처

    - [Merge 기초] git 공식문서 : https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-Merge-%EC%9D%98-%EA%B8%B0%EC%B4%88

    - [Merge 이해하기] datalibrary 님의 블로그 : https://datalibrary.tistory.com/194

    - [Merge 및 Conflict 다이어그램] sabarada 님의 블로그 : https://sabarada.tistory.com/196

    - [Merge 사용 예시] https://meetup.toast.com/posts/122

    반응형
Designed by Tistory.