svn과 달리 git은 브랜치의 개념을 도입하여 누구나 하나의 로컬에서 여러개의 로컬을 가질 수도, 여러 개의 원격지를 가질 수 있습니다.
그래서 보통 master를 두고 git flow 전략 ( ? ) 등을 사용하는 것으로 알려져있습니다.
하지만 이런 브랜치로 인해 merge 하는 방법론도 여러가지 있는데 크게 일반적 merge, rebase and merge, squash and merge 방식이 있습니다.
세 가지 방식 모두 병합이라는 측면에서는 같은 코드 결과물을 도출하지만 그래프라든지 merge하는 방식이 달라 간략히 알아보았습니다.
일반적인(?) 전통적인 merge
일반적인 merge는 1번 그림인데요, c에서 master로 merge한다면 master로 부터 분기되어진 parent로 되돌아가서
각 커밋 단위로 merge를 진행합니다. 진행 도중 conflict이 발생한다면 merge가 중단되고 conflict을 먼저 해결하라고 합니다.
이렇게 master의 head까지 도달한다면 merge가 완료됩니다. 또한 master 기준으로 merge 발생 시, 별도의 커밋이 생깁니다 ("feature/collection"에 변경점이 추가되었다는 커밋)
rebase and merge
이 방법은 rebase, 즉 브랜치의 parent를 master의 head로 바로 바꾸어 병합하는 방법입니다. master의 head로 만들기 때문에 rebase 시점에서 conflict이 발생할 것이고 rebase 후에 merge할 경우에는 반드시 conflict 없이 merge를 보장합니다.
rebase는 merge를 위해 쓰이기 보다는 내가 분기한 head를 최신화해야할 때 의도적으로 하는 개념이 맞는 것 같고 rebase and merge 방식을 일종의 선머지라고도 부릅니다. 또한 merge시, conflict로 인한 변경점이 없으므로 별도의 커밋이 생성되지 않습니다.
git rebase는 또한 -i 옵션 (interactive) 기능을 제공해 커밋들을 묶어 reword 키워드를 통해 rename하거나, squash을 하거나 rebase 대상에서 exception할 수 있는 장점이 있습니다.
squash and merge
1. 에서 설명한 방식과 동작 방식은 같습니다만, 브랜치에서의 커밋 모음을 squash해서 하나의 커밋처럼 만들어 합치는 방식입니다.
의미 없는 커밋들 ex '컬렉션 개발#1', '컬렉션 개발#2' 같은 커밋 단위들을 묶을 수 있어 잘못된 커밋이 있어도 amend나 cherry pick하지 않고
머지되는 과정에서 '컬렉션 개발'로 sqaush 한다면 master의 그래프를 아름답게 만들 수 있습니다.
개인적으로 3.의 방식은 2.의 rebase -i를 통해 대체가 되므로 1, 혹은 2,의 방법으로 머지하는게 좋은 것 같습니다.
Git Flow Rule을 따른다고 한다면 일반적으로
develop - feature간의 merge : 3. 방식을 사용해서 feature 브랜치에서의 커밋은 어쩔수 없게도 지저분하기 때문에 squash로 불필요한 그래프 노드를 최소화할 수 있다.
master - develop간의 merge : 2. 방식을 사용해서 master 그래프에 별도의 새로운 커밋을 생성하지 않도록 한다.
hotfix - develop, hotfix - master 간의 merge : 1. 방식을 사용해 별도의 커밋을 남겨 hotfix 이후에 코드 리뷰 회고 등에 활용한다.