2 분 소요


이전까지 프로젝트를 하면서 git을 계속 사용했지만 내부 동작 방식을 제대로 이해하지 못한채로 사용했었다.
내부 동작 방식을 몰랐기 때문에 예측할 수 없는 상황이 많이 발생했었다. 예를들어 merge를 했는데 언제는 commit이 생기고 언제는 안생기는 등…
이번 기회에 git에 대해서 정리하고자 한다.

merge

merge란 두 브랜치를 합치는 기능을 의미한다. 일반적으로 많이 사용되는 병합 방식으로, 커밋 이력을 모두 남길 때 사용한다.
merge 방식에는 Fast-forward, 3-way-merge 가 있다.
하나씩 살펴보자

Fast-forward 방식

img1

master 브랜치에서 hotfix 브랜치를 merge한다고 해보자.

img1

  1. 먼저 checkout 명령어를 사용해서 master 브랜치로 이동한다
  2. 그리고 hotfix 브랜치를 merge한다.

결과를 보면 fast-forward 라는 문구가 보인다. 이게 무엇일까?

hotfix가 가리키는 C4 커밋은 C2 커밋을 base로 하는 브랜치이다. 때문에 merge를 진행함에 있어서 새로운 커밋을 생성할 필요가 없다. 그저 단순히 브랜치 포인터만 이동시켜서 master가 c4를 가리키게 하면된다. 이 경우와 같이 두 브랜치의 base가 같을 때 단순히 브랜치 포인터를 이동시키는 merge 방식을 fast-forward 라고 한다.

3-way-merge 방식

이번에는 협업할 때 가장 많이 사용하는 merge 방식인 3-way-merge에 대해서 알아보겠다!

img1

my-branch가 main 브랜치의 B 커밋으로부터 분기됐다. 그리고나서 main에 새로운 커밋(C, D, E)이 생성된 경우를 생각해보자.
이 경우에는 fast-forward같이 단순히 브랜치 포인터를 이동시키는 방식으로는 merge를 제대로 수행할 수 없다.
때문에 my-branch와 main을 공통 부모로 하는 새로운 커밋을 생성한다. 이 방식을 3-way Merge 라고 한다.

fast-forward가 가능한 경우에도 Recursive Merge 방식처럼 커밋을 새로 생성하는 방법을 제공한다. git merge 명령에 –no-ff 옵션을 주면 강제로 commit을 생성한 merge가 진행된다.

Conflict

다음으로는 merge할 때 발생할 수 있는 Conflict에 대해서 알아보자.

img1

git checkout master

//hotfix, iss53이 동일한 파일을 수정.
git merge hotfix
git merge iss53
//-> 충돌 발생!!


다시 이 상황을 놓고 생각해보자. master 브랜치에서 필요한 기능이 있어서 iss53 브랜치를 생성해서 기능을 구현하고 있다.
그런데 도중에 master 브랜치에서 오류를 발견해서 hotfix브랜치를 만들어 오류를 수정하고 hotfix브랜치를 master 브랜치에 merge했다. 그런데 hotfix 브랜치에서 수정한 부분이 iss53 기능 브랜치에서 수정한 부분이 동일하다고 가정하자.
그러면 merge를 수행할 때, 충돌이 발생하게 된다.
이 merge를 실행하는 시점에 git은 활성화되지 않은 새로운 커밋을 하나 생성한다. 그리고 IDE를 사용하던, 직접 충돌 부분을 수정하여 commit을 하면 충돌이 해결된다.

지금까지 merge 방식과 충돌에 대해서 알아봤다. 그런데 github에서 제공하는 merge는 조금 다르다. 총 3가지 방식이 있는데 하나씩 살펴보자

3가지 pull request 방식

Create merge commit

img1

원래 기본적으로 base가 같을 때 merge를 수행하면 fast-forward 방식이 사용된다. 하지만 Create merge commit 방식은 새로운 commit을 만드는 방식을 사용한다.
이렇게 함으로써 기능 구현 후 merge 기록이 남게된다. 때문에 가독성이 높아진다.

Squash and merge

img1

이 방식도 Create merge commit 과 마찬가지로 새로운 커밋을 생성한다.
Create merge commit과의 차이점은 feature에서 작업한 모든 commit을 하나의 commit으로 통합해서 merge한다.
-> pr 단위로 commit이 묶이기 때문에 가독성이 높아진다.

Rebase and merge

img1

이 방식은 merge 하는 모든 commit을 단순히 복사해서 붙여넣는다.
중간의 merge과정이 보이지 않고 history가 한줄로 나열되기 때문에 오히려 가독성이 떨어질 수도 있다.

이번에는 merge와는 조금 다른 병합 방식인 rebase에 대해서 알아보겠다.

Rebase

현재 브랜치의 base를 재설정하여 합치는 것을 말한다.

img1

특징

Merge와 동일하게 브랜치를 합치는 목적으로 사용된다. - Commit History가 Merge와 다르게 선형적으로 그려진다.

Rebase는 현재 브랜치의 base를 바꾸는 것이다. - 기존의 Commit을 복사하여 새로 생성한다.



Cherry-pick

다른 브랜치의 있는 commit을 현재 브랜치에 적용하는 것을 의미한다.
일반적으로 새로운 브랜치를 하나 파서 cherry-pick을 진행하고 원격 저장소에 push하고 main으로 pr을 날리는 것이 일반적이다.

//한개의 커밋 가져오기
git cherry-pick 39c123

//여러개의 커밋 가져오기
git cherry-pick 39c123..bjkd12

Cherry-pick Conflict

해결 방법

  1. Conflict 발생 파일을 수정한다.
  2. git add 명령어로 수정된 코드를 스테이지 상태로 변경시킨다.
  3. git cherry-pick –continue 명령을 실행한다.



참고 사이트

  • https://www.youtube.com/watch?v=b72mDco4g78

  • https://www.zehye.kr/git/2019/11/22/11git_merge_conflict/

  • https://hudi.blog/git-merge-squash-rebase/