프로그래밍/Git

[Git] merge의 종류 (Fast forward, 3-way merge)

merge는 저에게 항상 예측 불허한 존재였습니다.

언제 merge 커밋이 생기고, 왜 충돌이 발생하는지 그 원인을 몰랐기 때문입니다.

그래서 이 글을 통해 merge의 개념과 방식을 정리해보고자 합니다.

 

아래 자료들을 참고했습니다.

 


Fast forward merge

Before

Before Fast forward merge

$ git checkout master
$ git merge hotfix

Updating f42c576..3a0874c
Fast-forward
 ...

 

master 브랜치에선 작업이 없었기 때문에 여전히 hotfix 브랜치와 동일 선상에 있습니다.

이때 merge를 하면 master 브랜치의 HEAD가 hotfix 브랜치의 HEAD로 이동합니다.

마치 Fast forward(빨리 감기) 되듯 말이죠.

 

 

After

After Fast Forward merge

Fast forward는 별도의 merge 커밋이 생성되지 않습니다.

브랜치를 비교할 필요가 없기 때문에 충돌이 발생할 일도 없구요.

 

 


3-way merge (Recursive merge)

3-way merge는 대부분의 협업에서 발생하게 되는 merge 방식입니다.

아래 그림처럼 두 브랜치가 동일 선상이 아닐 때 3-way merge가 발생합니다.

 

Before

Before 3-way merge

 

 

먼저 두 브랜치가 분할되는 기점에서 공통 조상을 찾습니다.

 

Before 3-way merge (C2 = 공통 조상)

 

$ git checkout master
$ git merge iss53
Merge made by the 'recursive' strategy.

이후 3-way merge를 진행합니다. (자세한 원리는 하단에 소개)

이 과정에서 두 브랜치의 커밋과 공통 조상의 커밋, 총 3개의 커밋이 관여하기 때문에 3-way라 불리게 됩니다.

 

 

After

After 3-way Merge (C6 = merge 커밋)

3-way merge가 완료되면 그 결과를 담고 있는 merge 커밋이 생성됩니다.

이 merge 커밋은 가리키고 있는 부모가 여러 개라는 특징이 있습니다.

 

 

3-way merge 원리

3-way merge 원리 - 물음표는 충돌을 의미 (생활코딩)

3-way merge 방식은 꽤나 효율적이고 흥미롭습니다.

바로 공통 조상(Base) 덕분인데요.

 

먼저 2-way merge 방식을 보겠습니다.

2-way merge는 단순히 두 브랜치만 비교하는 방식입니다.

비교 기준인 Base가 없기 때문에 두 브랜치의 다른 부분들은 충돌로 판단하게 됩니다.

 

다음으로 3-way merge 방식을 보겠습니다.

Base를 기준으로, 변화가 발생했다면 이를 merge 결과로 채택합니다.

이때 만약 두 브랜치가 각기 다른 변화를 발생시켰다면 이를 충돌로 판단하게 됩니다.

이 충돌을 해결한 뒤에 merge를 하면 3-way merge가 완료됩니다.

 

 


Merge 옵션

1. --ff

merge의 default 값이며, 위에 설명한 방식과도 같습니다.

즉 Fast forward면 새로운 커밋을 생성하지 않고 Fast forward가 아니면 merge 커밋을 생성합니다.

$ git merge --ff master  # --ff 생략 가능

 

2. --no-ff

Fast forward 관계인 경우에도 무조건 merge 커밋을 생성합니다.

$ git merge --no-ff master

$ git log --oneline
927916b (HEAD -> master) Merge branch 'master'

 

3. --squash

스퀴시란 모든 커밋을 하나의 커밋으로 합친 뒤 merge를 진행합니다.

커밋 히스토리를 깔끔하게 관리할 수 있는 장점이 있습니다.

$ git merge --squash master