Git

[Git] merge rebase git-flow

개발조무사 윤뚜비 2022. 12. 19. 09:36

Merge commit 이란?

협업을 하다 보면 하나의 비중 있는 브랜치를 단독적으로 사용하는 것이 현실적으로 불가능.

그래서 따로 해당 이슈에 맞게 브랜치를 [ex) my-branch] 따와서 해당 이슈에 관한 코드를 작성해야 함.

이후 내 브랜치의 작업 수정본을 기존 브랜치 [ex) master]에 커밋을 합쳐주어야 함.

이 과정을 병합(merge)라고 한다.

 

내가 수정하고 있는 사이 다른 사람이 코드를 master에 커밋하고 푸시해버렸다면

나의 브랜치와 master가 바라보는 지점이 다르게 돼버린다. 쉽게 말하면 내 브런치가 시대에(?) 뒤처져있다.

객관적으로 바라본다면 아래의 상황처럼 될 것이다.

 

처리시간이 빨라서 다른 사람이 master 브랜치에 손대기 전이라면 위 그림처럼 충돌 없이 (코드/커밋)가 잘 합쳐진

겠지만 아무래도 협업이라는 게 하다 보면 같은 부분의 코드를 수정하는 일이 빈번하게 일어난다.

 

규모가 크면 더더욱 그럴 것이다.

이 때는 merge시 충돌(conflict)이 일어나게 된다.

그때는 Git이 임의로 합칠 수 없어 사용자가 기존 커밋과 나의 커밋의 diff를 체크하고

충돌이 나지 않게 해결해주어야 정상적으로 merge가 가능해진다.

 

Merge와 Rebase 차이점

Rebase는 내가 따로 브랜치를 따와서 작업했던 커밋들이 merge시 마지막에만 합쳐지는 게 아니라 기존 master 브랜치에 내가 작업했던 커밋들이 1열로 추가되는 것이다. 아래 그림을 보면 이해하기 쉬울 듯하다.

이렇게 되면 표면상 Merge기록이 남지 않고 아래와 같이 하나의 브랜치에서 작업한 것처럼 깔끔하게 보인다.

 

 

리베이스의 장점과 단점

우선 단점부터 보자면.

단순 Merge Commit을 사용하면 기존 개발자들에게 익숙한(?) 개념이면서 merge 된 브랜치가

삭제되어 사라졌어도 커밋 히스토리 그래프상에는 여전히 다른 분기된 브랜치로 표기가 되기 때문에

어떤 브랜치에서 어떤 커밋이 되었고 어떻게 merge가 되었는지 자세한 정보를 얻을 수 있다.

 

이는 문제 발생 시 추적이 디테일한 추적이 가능하다는 말씀.

하지만 리베이스의 경우 그래프를 하나로 합쳐버리기 때문에 커밋 메시지에 최대한 정보를

담지 않는다면 추적에 어려움을 겪을 수 있다. 이를 보완하기 위해서는 최대한 알아먹기

쉽게 커밋 메시지를 작성하면서 작업 단위를 잘 구분해서 커밋해야 한다.

 

반면에 너무 세세한 수정 부분까지 히스토리가 남게 되면 추후 간단하게 분석하고자 할 때는

그래프의 가독성이 떨어지면서 내가 자주 하는 실수처럼 간단한 오타를 수정하고

다시 커밋하게 되는 경우 의미 있는 커밋 메시지의 논점이 흐려지기 때문에 Rebase를 사용하는 경우

스쿼시를 통해 크게 의미 없는 커밋들은 하나로 묶어주는 게 좋을 듯하다.

 

 

요약

merge commit은 브랜치 정리가 안되어있어 가독성은 떨어지지만 세세하게 추적할 수 있고

rebase commit은 한 브랜치에서 작업한 것처럼 보여 히스토리의 가독성은 좋지만 디버깅 시 변경사항을 세세하게 추적하는데 어려움이 있다.

고로 Rebase를 사용한다면 스쿼시를 통해 의미 없는 커밋들을 통합해 최대한 의미 있는 단위로 커밋을 만들어 사용하면 좋을 듯하다.

 

물론 히스토리를 뒤져볼 필요가 없게 최대한 꼼꼼하게 작업하는 게 Best가 아닐까.

 

 

 

Fast-Forward, No-Fast-Forward? (feat. —ff,—no-ff)

결론부터 말하자면 이전 커밋이 이후 커밋의 부분집합 일 경우 별도의 과정 없이 커밋을 옮길 수 있는지에 대한 차이다.

아래 이미지를 한번 보자.

 

몇 가지 예제로 우선 틀을 잡아보자

A → D : FF

A → G : FF

B → E : FF

C → E : FF

F → E : NO-FF

G → D : NO-FF

위 그림을 정리해서 집합을 그림으로 표현하면 아래와 같다.

그렇다면 FF일 때와 NO-FF일 때 그래프 변화를 한번 보자.

 

 

 

현재 Dev 브랜치에 있다고 생각하고 아래 커맨드를 실행해보자.

git merge Master

위 그림처럼 별도의 새로운 커밋 없이 같은 커밋을 참조하게 된다.

 

 

마찬가지로 NO-FF 옵션을 설정해주고 커맨드를 날려보자.

git merge —no-ff Master

 

 

 

 

위 그림처럼 Master 브랜치를 포함한 새로운 커밋이 생기고 Dev는 그곳을 참조하게 된다.

 

 

Tag

 

레파지토리의 버전 정보를 간간히 표기하는 방법으로 커밋 메시지나 브런치 네이밍에 첨부할 수 있지만

태그로 다는 게 보기 좋고 깔끔해 보인다.

JetBrain IDE 기준 커밋을 우클릭하고 태그를 추가하면 아래와 같이 회색으로 네임택(?)이 생긴다.

전체 태그 목록을 보려면 아래 커맨드를 입력한다.

 

git tag

 

이렇게 해당 태그를 기반으로 해당 버전에서는 어느 기능까지 구현이 되었나 확인하면 되겠다.

 

 

 

Git Flow 전략

1인 개발 프로젝트이거나 사이드 프로젝트 규모라면 아래처럼 혼자 Master 브랜치에서 작업을 하는 게 매우 편하다.

머지할 때 서로 충돌 일으킬 일도 확연히 적어지고 그래프가 보기 깔끔하기에 많이들 사용하는 방법이다.

 

 

하지만 실무에 들어가 실제로 서비스를 운영한다고 생각해보자.

실제 운영되는 릴리즈 서버의 코드와 개발자들이 테스트하고 실험하는 코드가 같은 선상에 놓여있다면

개발자가 실수하는 과정에서 사용자에게 까지 영향을 미치게 되고 사용자의 서비스는 불안정 해지고 이는 사용자 이탈 현상으로 이어진다.

그래서 보통 실제 운영되는 릴리즈 서버와 개발서버를 따로 두는 게 일반적이다.

간단하게 두 가지만 분기하면 아래와 같은 그림이 나온다.

 

개발서버에서 필요한 기능을 여러 차례에 나눠 개발해두고 일정에 맞게 릴리즈 서버 브랜치로 merge 해버린다.

물론 이와 같은 경우도 매우 간단한 편이고 살짝 더 나눠보자면 기능별로 나누거나 QA전용 테스트

서버가 따로 존재하기도 한다.

 

아래처럼 분기해서 작업하면 브랜치 병합이나 테스트 과정에서 대부분의 오류를 확인할 수 있기 때문에

실제 사용자에게 오류가 생길 가능성을 확연히 낮출 수 있다.

 

위 그림처럼 각각의 기능마다 브랜치를 따서 작업하고 이를 DEV 브랜치에서 한번 합치고 테스트 후

인력풀이 여유롭다면 QA 브랜치로 한번 합치고 테스트를 마친 후 릴리즈 서버로 배포한다.