ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • git merge, rebase merge, squash merge
    카테고리 없음 2022. 8. 16. 11:12

    dev 와 feature 브랜치로 여러 개념을 이해해보자! (merge의 기본 단위는 branch)


    dev : base branch

    feature : dev 브랜치에서 나눠진 하위 브랜치

     

    오늘 생각해볼 것 ✏️

    • merge
      • fast-forward merge
      • non fast-forward merge
    • github에서의 여러 가지의 merge
      • merge
      • rebase and merge
      • squash and merge



    ☁️ fast-forward merge

    feature브랜치를 dev브랜치로 병합할 때 dev브랜치에 새로운 작업 내역이 없다면 merge commit 없이 HEAD만 변경할 수 있다. 이를 fast-forward(빨리 감기) merge라고 부른다.

    • 현재 브랜치(feature)의 HEAD를 대상 브랜치(dev)의 HEAD로 옮긴다.
    • 이는 github에서 지원하지 않는다. - 참고
    • 우리팀은 보통 github에서 merge를 진행하므로 fast-forward는 못 쓸 것 같다.

    • fast-forward merge의 결과물 !

    즉, 상위 브랜치에 변경사항이 없을 때만 fast-forward merge가 가능하다.

    ☁️ non fast-forward merge

    • 만약 merge하려는 상위 브랜치에 변경사항이 있다면?
    • 이런 경우에는 양쪽의 변경을 가져오는 merge commit이 있어야 한다.

    feature 브랜치에서 작업을 완료하고 dev 브랜치로 합치려는데, dev 브랜치에 다른 협업자들이 작업한 결과물이 새로 commit되어 dev 브랜치가 달라졌다면?

    • feature 브랜치를 dev에 merge하기 위해서는 dev 브랜치 내의 변경 내용과 feature 브랜치 내의 변경 내용을 하나로 통합할 필요가 있다.
    • 따라서 양쪽의 변경을 가져온 merge commit을 실행하게 된다.

    • 이를 non fast-forward merge라고 한다. non fast-forward merge를 하게 되면 브랜치가 그대로 남기 때문에 그 브랜치로 작업한 내용들을 확인하기에 유용하다.

    ☁️ github에서의 여러가지 merge

    나는 보통 github 페이지에서 쉽게 merge를 진행한다.

    github 페이지에서 할 수 있는 merge는 세가지가 존재한다.

    1. create a merge commit
    2. squash and merge
    3. rebase and merge

    위와 같이 존재하는데 merge 버튼 옆에 있는 토글을 열면 merge option 들을 확인할 수 있다. 기본으로는 create a merge commit 으로 설정되어 있다.

    1️⃣ create a merge commit

    앞에서 설명한 non fast-forward이다. base 브랜치로 merge를 진행할 때 merge commit을 통해 합친다.

    merge 버튼을 누르면 merge commit을 작성하는 칸이 나온다.

    2️⃣ squash and merge

    다음은 squash and merge인데, squash의 사전적 의미는 대충 과일 같은 것을 쭈욱 찌부시키는 것이다. 과일 대신.. commit을 생각해보자!

    dev 브랜치로 merge하는 상황을 가정했을 때

    이런식으로 merge되는 것이 squash merge이다.

    feature 브랜치의 x, y, z 커밋을 하나로 쭈욱 합쳐서 dev 브랜치로 merge할 수 있도록 해준다.

    • x, y, z를 합쳐서 새로운 커밋을 만든다.
    • 이 새로운 커밋은 dev 브랜치의 HEAD를 parent로 갖는다.

    squash and merge는 어떨 때 유용할 까?

    나는 이번 프로젝트에서 대댓글 기능 구현 역할을 맡았다.

    프론트로서 대댓글 기능 구현을 하기 위해서는 여러 commit을 할 것이다.

    UI 만들어야지 ..
    mocking 해야지 ..
    함수 연결해야 하지 ..
    어 스타일 맘에 안 드네 고쳐서 commit 해야징 ..

    이런식으로 한 기능당 여러 commit이 발생할 수 있는데

    이 commit들이 dev 브랜치 commit 내역에 남는다면 기능 별로 commit log를 확인하는 데에 있어 불편함을 격게 되고, 미관상으로도 예쁘지 않다.

    그런데 sqaush and merge 기능을 이용하게 된다면? 내가 feature에서 작업한 작업들이 하나의 커밋으로 합쳐지게 된다.

    예를 들면 기능 하나로 커밋이 쭈악 합쳐진다. (dev 브랜치 commit log들이 아주 예뻐짐)

    내가 feature 브랜치에서 진행한 commit들은 합쳐진 commit 하나 속 details로 남겨진다.

    3️⃣ rebase and merge

    rebase and merge는 뭘까? 일단 이를 알기 전에 rebase가 뭔지 알아야한다.

    rebase

    두 개의 공통 base를 가진 branch에서 한 branch의 base를 다른 branch의 최신 commit으로 base를 옮기는 작업! 이름에서 알 수 있듯이 re 다시 base를 설정하는 것이다.

    예시로 한번 이해해보자

    현재 나는 feature 브랜치에서 작업을 진행하고 있다.

    feature 브랜치는 dev 브랜치로부터 파생된 브랜치이며 b commit까지 쌓인 이후 나는 feature 브랜치를 생성했다.

    feature 브랜치에서 x, y commit을 하며 열심히 작업하고 있었는데

    나의 동료 개발자가 c, d라는 작업을 완료해서 dev에 머지를 했다.

    dev 브랜치는 그 전과 작업 내용이 달라졌기 때문에 내가 작업을 완료하고 dev로 merge할 시기에 충돌이 날 가능성이 있다. 이때 rebase를 활용할 수 있다.

    rebase를 활용하면 내 브랜치의 base commit은 b지만 dev의 최신 작업 내용인 d로 base commit을 변경할 수 있다! 즉, 전과 달리 위와 같은 그림이 된다.

    base를 바꾼다는 것은 알겠는데 rebase를 활용하면 얻는 이점은 무엇일까?

    1. dev 브랜치의 최신 변경사항을 즉각 반영할 수 있다. (즉, 최신 dev의 최신 commit을 반영하면서 작업을 해야할 때 좋다.)

    예를 들어, 나는 대댓글을 작업하고 있고 동료 개발자는 댓글 부분을 수정하고 있다고 가정해보자. 대댓글을 작업하는 동시에, 동료 개발자의 dev 브랜치로 merge가 완료된 작업을 rebase를 통해 바로 이어받아 작업을 진행할 수 있다.!

    2. commit 이력을 남기지 않아 깔끔한 commit history를 유지할 수 있다.

    만약 동료 개발자가 한 작업(dev에 merge됨)이 나와 코드상에서 충돌이 발생한다면?

    rebase를 활용하지 않는다면 충돌을 해결한 merge commit을 push 해야해서 commit history가 더러워진다. 하지만 rebase를 활용하면 내 commit들이 마치 충돌이 발생하지 않았던 것처럼 동료 개발자 commit 뒤에 위치하게 되므로 깔끔하게 commit history를 유지할 수 있다.

    어 그런데 rebase를 했더니 계속 같은 충돌을 해결하래요 ㅠㅠ 왜이래 이거

    처음 rebase를 알았을 때 계속 충돌이 나서 나는 내가 뭘 잘못 만졌구나..만 생각했었다.

    하지만 이는 rebase의 과정을 생각해보면 이해할 수 있다.

    이 그림에서 다시 생각해보자.

    만약 동료 개발자가 merge한 pr의 작업(c, d)들이 나와 충돌이 나는 작업이었다면?

    rebase를 딱 시작했을 때, 내 commit은 x, y가 존재한다. 내 x, y는 모두 b를 기준으로 작업했기 때문에 x, y 둘 다 d 뒤로 보내주기 위해서는 x, y 둘다 충돌을 해결해주어야 한다!

    이런 이유로 계속 충돌을 해결하라는 메시지가 떴던 것이다!

    이는 rebase의 단점으로 꼽힌다. commit 마다 충돌처리를 해주어야 하기 때문이다.

    다시 rebase and merge로 돌아와보자.

    rebase and merge를 진행하면 위와 같은 그림이 그려질 것이다.

    x commit의 base를 b에서 d로 변경하며 merge가 된다.

    rebase and merge를 활용하면 merge commit 없이 merge를 진행할 수 있고,

    squash merge와는 다르게 하나의 커밋으로 합쳐지지 않고 pr의 commit 하나하나 살아서 dev 브랜치로 merge 된다.



    결론

    우리팀에서는 rebase를 한 후, squash and merge하는 전략을 사용하고 있다.

    만약 슬랙에서 동료 개발자 혹은 나의 PR이 merge되었다는 알림이 오면 rebase를 진행한다.

    git fetch origin dev
    git rebase origin/dev 

    위와 같이 입력하고 rebase를 진행하며 충돌이 있다면 충돌을 하나하나 해결해준다.

    (만약 IDE에 현재 작업 내용이 있다면 stash를 활용한다.)

    rebase를 활용하면 최신 작업내용을 가져올 수 있고, 충돌 해결을 위한 commit이 없어져서 활용하고 있다.

    이후, squash and merge를 진행하는데 이러면 dev에 기능별로 commit이 쌓이기 때문에 commit log가 깔끔해져 사용하고 있다.

    이런 느낌 ~




    참고

Designed by Tistory.