[git] 브랜치와 Git Merge 방법

2024. 3. 20. 13:22Git&GitHub

브랜치와 Git Merge 방법

 
이번에는 브랜치와 Git Merge에 대해 살펴보겠습니다. 주로 git merge 명령어를 사용해온 경험을 바탕으로, 다양한 병합 방법과 각 방식의 차이점을 비교해보겠습니다. Git에서 브랜치 관리는 협업과 버전 관리를 효율적으로 진행하는 데 매우 중요합니다. 그럼 다양한 병합 방법과 그 특징을 살펴보겠습니다.
 

🔍 브랜치란?


git merge 방법에 대해 알기 위해서는 먼저 브랜치와 커밋 히스토리에 대한 이해가 필요합니다.  Git 브랜치 - 브랜치란 무엇인가를 참고해서 정리해 보겠습니다.


✅ 브랜치는 원래 코드와 상관없이 독립적으로 개발을 진행하기 위한 것입니다. 원래 코드와 동일하지만, 분리되어 있기 때문에 부담 없이 작업할 수 있습니다.
 

🔍 Git이 데이터를 다루는 법


git의 동작 방식에 대해서도 알아보겠습니다.

💡Git은 데이터를 Change Set이나 변경사항(Diff)으로 기록하지 않고 일련의 스냅샷으로 기록한다.

커밋하면 Git은 현 Staging Area에 있는 데이터의 스냅샷에 대한 포인터, 저자나 커밋 메시지 같은 메타데이터, 이전 커밋에 대한 포인터 등을 포함하는 커밋 객체(Commit Object)를 저장한다. 이전 커밋 포인터가 있어서 현재 커밋이 무엇을 기준으로 바뀌었는지를 알 수 있다. 최초 커밋을 제외한 나머지 커밋은 이전 커밋 포인터가 적어도 하나씩 있고 브랜치를 합친 Merge 커밋 같은 경우에는 이전 커밋 포인터가 여러 개 있다.

 

시간순으로 프로젝트의 스냅샷을 저장하는 git

➡️ 파일의 변경 사항을 저장하는 것이 아니라, 커밋하는 시점의 프로젝트 폴더 자체를 스냅샷처럼 저장한다는 것입니다. 단, 이전 커밋과 비교하여 달라지지 않은 파일은 성능을 위해서 이전 상태의 그 파일에 대한 링크만 저장합니다.


➡️ 커밋하는 시점의 파일과 디렉터리 구조는 트리(tree)라는 객체에 저장되고
➡️ staged 파일은 blob으로 저장됩니다.
➡️ 커밋 객체는 트리를 가리키는 포인터와 메타데이터를 가집니다.


➡️ 각 스냅샷은 포인터로 연결되어 부모-자식 관계를 알 수 있습니다.

Git의 브랜치는 커밋 사이를 가볍게 이동할 수 있는 어떤 포인터 같은 것이다. 기본적으로 Git은 main 브랜치를 만든다.
처음 커밋하면 이 main 브랜치가 생성된 커밋을 가리킨다.
이후 커밋을 만들면 main 브랜치는 자동으로 가장 마지막 커밋을 가리킨다.

 

📍git merge 방법


Git 브랜치 - 브랜치와 Merge의 기초를 참고해 작성했습니다.

🔍  fast-forward merge

 

머지 전 커밋 히스토리

➡️ master 브랜치에서 분기된 hotfix 브랜치를 master 브랜치에 merge 할 때, master 브랜치에 새로운 커밋이 없다면 브랜치 포인터는 merge 과정 없이 (merge 하려는 브랜치의 최신 커밋으로) 이동합니다.

➡️ 이를 "fast-forward-merge"라고 하며, merge 메시지에서도 확인할 수 있습니다.

fast-forward 된 후의 커밋 히스토리

💡똑같이 merge를 해도 merge 커밋이 생성되는 경우가 있고, 그렇지 않은 경우가 있었는데 merge 방식의 차이 때문이었습니다.

🔍  3-way merge

머지 전 커밋 히스토리

➡️ 3-way merge: 각 브랜치가 가리키는 커밋 두 개와 공통 조상 커밋 하나를 합치는 것.

머지 후 커밋 히스토리

➡️ 세 개의 커밋이 합쳐져 "merge 커밋"이 만들어지고, 브랜치의 포인터가 그 커밋을 가리키도록 이동합니다.


🔍  rebase and merge

💡rebase: base를 바꾸는 것, 해당 브랜치가 시작된 커밋의 위치를 변경한다.
💡시작점을 변경하면, fast-forward merge가 가능하기 때문.

다음과 같이 merge 시킬 브랜치로 이동한 후, master(main) 브랜치로 rebase 합니다.

rebase 전 커밋 히스토리
rebase 후 커밋 히스토리.

실제로 일어나는 일을 설명하자면 일단 두 브랜치가 나뉘기 전인 공통 커밋(C2)으로 이동하고 나서 그 커밋부터 지금 Checkout 한 브랜치가 가리키는 커밋(C4)까지 diff를 차례로 만들어 어딘가에 임시로 저장해 놓는다.

Rebase 할 브랜치(experiment)가 합칠 브랜치(master)가 가리키는 커밋을 가리키게 하고
저장해 놓았던 변경사항을 차례대로 적용한다.

rebase 후 merge를 하면, fast-forward-merge 방식으로 병합됩니다. 병합 후 커밋 히스토리는 아래와 같습니다.

rebase and merge 완료 후 커밋 히스토리

C4'로 표시된 커밋에서의 내용은 Merge 예제에서 살펴본 C5 커밋에서의 내용과 같을 것이다. Merge 이든 Rebase 든 둘 다 합치는 관점에서는 서로 다를 게 없다. 하지만, Rebase가 좀 더 깨끗한 히스토리를 만든다. Rebase 한 브랜치의 Log를 살펴보면 히스토리가 선형이다. 일을 병렬로 동시에 진행해도 Rebase 하고 나면 모든 작업이 차례대로 수행된 것처럼 보인다.

 

주의할 점

이미 공개 저장소에 Push 한 커밋을 Rebase 하지 마라.
이 지침만 지키면 Rebase를 하는 데 문제 될 게 없다. 하지만, 이 주의사항을 지키지 않으면 사람들에게 욕을 먹을 것이다.
Rebase는 기존의 커밋을 그대로 사용하는 것이 아니라 내용은 같지만 다른 커밋을 새로 만든다.
새 커밋을 서버에 Push 하고 동료 중 누군가가 그 커밋을 Pull 해서 작업을 한다고 하자. 그런데 그 커밋을 git rebase로 바꿔서 Push 해버리면 동료가 다시 Push 했을 때 동료는 다시 Merge 해야 한다. 그리고 동료가 다시 Merge 한 내용을 Pull 하면 내 코드는 정말 엉망이 된다.

참고 : Git 브랜치 - Rebase 하기


 
이상으로 git 브랜치의 동작 방식과 merge 방법에 대해 알아보았습니다.

'Git&GitHub' 카테고리의 다른 글

[Git&GitHub] git clone과 git fork의 차이점  (0) 2024.02.05