Rebase와 Merge
이번 프로젝트에 git flow로 협업을 진행했는데 다른 브랜치와 병합을 대부분 merge로 진행하였다. 이때 약간 거슬리는 문제가 발생하였는데 불필요한 merge 커밋이 발생하고 커밋 히스토리가 어지러워져서 rebase와 merge의 차이점을 구분하고 왜 어떨 때 쓰는 건지 알기 위해서 이번 글을 작성하였다.
Merge와 Rebase가 무엇일까?
git은 작업을 해야할 때 브랜치를 생성하고 , 브랜치로 체크아웃해서 해당 브랜치에 커밋을 해서 작업한다. 기능이 완성되었다면 브랜치를 분기한 브랜치로 다시 체크아웃 한 후 작업한 브랜치와 분기된 브랜치의 코드를 병합해야하는 작업이 필요하다. 이때 git에서 병합을 위한 명령어가 바로 merge 와 rebase 이다.
git merge feature/child-server-sis92345
git rebase develop
merge와. rebase는 브랜치 병합을 수행한다는 목적은 같지만 병합을 수행하는 방법이 다르다.
merge는 현재브랜치에서 [병합하고자하는 브랜치]의 변경사항을 병합한다
rebase는 베이스를 재배치 한다
merge 개요
merge는 현재브랜치에서 [병합하고자하는 브랜치]의 변경사항을 병합한다. merge는 여러 종류가 있지만 99%는 아래 merge를 사용한다.
fast-forward-merge: 현재브랜치의 커밋이 없을 경우 현재 브랜치의head를 병합하고자 하는 브랜치의head로 옮기는 merge3-way-merge: 현재 브랜치의 커밋과 병합하고자하는 브랜치의 조상 커밋이 다를 경우 공통 조상 커밋을 사용하여 병합을 진행하는 merge이다. 즉 현재 브랜치의 head, 병합하고자 하는 브랜치의 head, 공통 조상 커밋(base)를 사용하기 때문에 3 way merge
Fast-forward merge의 예

-
위 상황에서 master 브랜치의 커밋이 없으므로 master와 dev를 merge하면 fast-forward merge가 진행된다

- Fast-forward merge가 진행된 예


3 way merge의 예
만약 fast-forward merge가 불가능한 상황이라면 대부분 3 way merge로 진행된다. 어플리케이션의 구조가 클 경우 대부분의 merge가 3 way merge로 진행된다. 3 way merge는 현재 브랜치 해드의 커밋 조상과 병합할 브랜치의 커밋 조상이 다를 경우 각 head와 공통 커밋 조상을 이용하여 merge를 진행한다. 아래 사진의 1번을 보자

- Master 브랜치 해드의 커밋 조상은 m4 , dev 브랜치의 커밋 조상은 a1을 가르킨다. 이 경우 fast-forward merge가 불가능 하므로 master 테이블의 head와 dev 테이블의 head와 두 커밋의 공통 조상인 m4(베이스) 3개의 커밋을 이용하여 병합을 진행 한 후 2번 상황의 c1 병합 커밋을 새로 생성한다.
그러면 도대체 rebase는 무엇인가
사실 merge만 사용해서 브랜치를 병합해도 문제는 없다. 다만 merge는 3 way merge처럼 병합한 커밋을 새로 생성하므로 커밋 히스토토리가 지저분해 질 수 있다. 반면 rebase는 base를 재설정하므로 커밋 히스토리를 하나로 만들 수 있다. 아래가 실제 프로젝트에서 merge만 사용해서 관리한 커밋 히스토리이다.

-
규모가 큰 프로젝트일 경우 merge만 사용해서 remote repository에 push할 경우 commit history가 지저분해진다. 그래서 커밋 히스토리를 단순하게 할 경우 브랜치 병합 전략을 Rebase and Merge를 사용해서 커밋을 한다.
-
rebase 활용
- commit history를 단순하게 만든다
- 원하는 브랜치의 커밋만 병합하고자 할 때
- Master 브랜치에서 client 브랜치가, server 브랜치에서 client 브랜치가 분기되었다고 하자
- 내가 server 브랜치만을 병합하고자 할 때 rebase를 사용할 수 있다
git rebase --onto master server client- git master 브랜치에서 부터 server와 client 브랜치의 공통 조상 커밋을 client에서 없앤 후 client의 베이스를 master로 재배치
rebase의 예
- 3 way merge를 이용할 경우 병합 커밋을 생성한다(C1). rebase를 브랜치의 베이스를 변경하여 병합을 진행한다.계속 나오는 베이스란 브랜치를 분기한 commit을 말한다. rebase는 병합될 브랜치의 커밋을 임시 저장소인 patch로 저장한 후 베이스를 병합할 브랜치의 head로 옮긴 후 patch에 임시 저장된 커밋을 옮겨진 베이스에서 다시 병합시키는 방법을 말한다. 따라서 병합된 브랜치의 커밋은 병합 할 브랜치의 head보다 앞서게 되며 이 상태에서 병합 할 브랜치는 병합 될 브랜치와 fast-forword merge가 가능한 상황이 된다.

-
따라서 위 상황을 rebase하면 다음과 같은 과정이 일어난다
- A1, A2 커밋을 patch에 복사
- dev 브랜치의 베이스를 master 브랜치의 head인 M5로 변경
- M5에서 다시 A1, A2 커밋을 병합하는 작업을 진행
- 최종적으로 master를 fast-forword merge가 가능한 상황이 된다.

-
git -c credential.helper= -c core.quotepath=false -c log.showSignature=false -c core.commentChar= rebase master로 rebaseTest와 master 테이블을 rebase한 후 master로 체크아웃 한 후 fast-forword merge를 한 상황

커밋 히스토리를 통한 비교
- Merge

-
rebase
-
rebase후 Fast forword merge를 하기 전

-
Fast forword merge를 한 후

-
📌 절대 remote 브랜치에 push된 브랜치에 rebase를 하지 않는다.
rebase할 때 절대 하지 말아야 할 위험 요소가 있다.
“Do not rebase commits that exist outside your repository and that people may have based work on.” “다른 동료가 작업 중인 외부에 공개 된 저장소 브랜치를 대상으로 리베이스하면 안됩니다.”
rebase해서 생성된 커밋은 내용은 같지만 다른 커밋을 새로 만든다. 즉 a라는 사람이 feature 브랜치를 merge한 후 reset한 후 rebase한다면 중복된 커밋이 존재한다. 자세한 사항은 아래 링크를 참고
https://git-scm.com/book/ko/v2/Git-브랜치-Rebase-하기