본문 바로가기

Deep Dive

[Git] merge와 rebase

 

 

merge와 rebase를 설명하기에 앞서 diff의 개념부터 짚고 넘어가면 좋을 것 같다.

 

👉 diff 란?

- Git에서 두 상태(커밋, 브랜치, 워킹 디렉토리 등)를 비교하여 그 차이점을 보여주는 명령어로,

주로 파일의 변경 내용(추가되거나 삭제된 코드, 수정된 부분 등)을 확인하는 데 사용된다.

 

만약, Git에서 commit 명령어를 실행하면,

이전 commit와 현재 commit을 비교하여 스냅샷(해당 시점의 파일 상태)을 저장하게 된다.

여기서 diff는 하나의 스냅샷과 다른 스냅샷 사이에 어떤 변경 사항이 있었는지 차이점을 보여준다.

 

* 사용 예시

만약 워킹 디렉토리와 스테이징 영역을 비교(diff 확인)하고 싶다면, 아래 명령어를 입력하면 되고

git diff

 

스테이징 영역과 최신 커밋 간의 비교(diff 확인)를 하고 싶다면, 아래 명령어를 입력하면 된다.

git diff --staged

 


 

👉 merge 란?

- 두 브랜치(branch)를 하나로 통합하는 데 사용되는 명령어로,

일반적으로 한 브랜치에서 작업한 내용을 다른 브랜치에 병합할 때 사용된다.

 

 

💀 merge를 왜 해야 하는데?

 

merge가 왜 진행되어야 하는지 궁금했는데, 브랜치의 특징을 이해하니 merge가 왜 필요한지 알 수 있었다.

 

아래에 master 브랜치에서 새로 생성된 dev 브랜치에서 merge를 진행하는 과정을 적어보았다.

더보기
더보기

1. master 브랜치에 commit 1, commit 2가 있다고 가정한다.
2. commit 2 시점에서 새로운 dev 브랜치를 생성한다.
3. dev 브랜치는 master 브랜치와 동일한 상태를 가지고 있다. -> commit 1, commit 2를 포함
4. dev 브랜치에서 새로운 작업을 진행한 뒤, commit abc라는 커밋을 진행한다.
5. dev 브랜치에서 새로운 커밋을 생성했지만, master 브랜치는 이 사실을 모른다. -> 외부 가지에서 생성했기 때문
6. master 브랜치에서도 commit abc를 반영하려면, dev 브랜치에서 merge 작업을 진행해야 한다.
7. merge 작업을 진행하면, master 브랜치와 병합되어 master 브랜치도 commit abc를 포함하게 된다.

 

나무🌳에 비유하여 정리해 보자면, 

브랜치는 나무의 몸체(master 브랜치)에서 뻗어나간 가지(dev 브랜치)인데,

해당 가지에서 여러 가지(새로운 커밋)들이 뻗어나가도 나무 몸체는 그 가지들이 뻗어나가는지 모른다.

이때 나무 몸체가 새로운 가지들의 존재를 알게 하고 싶다면?! 가지(dev 브랜치)에서 merge 작업을 진행해 주면 되는 것이다.

 

 

 

🍪 merge와 관련하여 알아두면 좋은 사실!

 

1. merge 작업 시, base를 기준으로 병합한다. 🙌

- Git은 두 브랜치를 비교하여 공통 조상을 찾는데, 이 공통 조상이 바로 "base"다.

여기서 "base"는 두 브랜치가 갈라지기 전의 마지막 커밋이 되며, 이를 기준으로 하여 두 브랜치의 차이를 계산하고 병합한다.

 

2. 병합 시 충돌이 발생했어요. 🤪

- 두 브랜치에서 동일한 파일의 동일한 부분이 수정된 경우 충돌이 발생할 수 있다.

이 경우 Git은 자동으로 병합하지 못하며, 사용자가 수동으로 충돌을 해결해야 한다.

사용자가 수동으로 충돌을 해결하고 나서는, git add로 수정된 파일을 스테이징하고, git commit으로 병합을 완료하면 된다.

 


 

👉  rebase 란?

- Git에서 브랜치의 커밋 이력을 다른 브랜치의 커밋 이력에 재배치하는 데 사용하는 명령어로,

rebase를 진행하면 기존 커밋의 해시가 변경되는데, 이는 초반에 설명했던 diff와 밀접한 관련이 있다.

 

그리고 rebase는 커밋 이력을 깔끔하게 만드는 데 유용하지만, 협업 시에는 주의가 필요하다.

특히, 이미 다른 사람들이 작업한 커밋을 rebase하게 되면, 협업에 문제가 생길 수 있다.

 

 

 

👾 rebase와 diff의 상관관계

 

rebase를 쉽게 말하자면, 기존 커밋의 부모를 새로운 베이스(기준점)로 변경하고, 그 위에 커밋들을 다시 쌓는 작업이라고 볼 수 있다.

 

중요한 것은 해당 작업(커밋들을 다시 쌓는 작업)에서 커밋의 부모가 변경되므로 -> 커밋 해시도 당연히 변경된다.

Git의 커밋 해시는 부모 커밋의 해시 + 스냅샷(diff 적용 결과)까지 포함한 정보를 바탕으로 생성되기 때문이다.

좀 더 구체적으로 커밋 해시는 부모 커밋의 해시, 커밋 메시지, 작성자 정보, 커밋된 파일의 스냅샷 등이 포함된 정보로부터 생성된다.

 

따라서 rebase를 통해 커밋을 다른 부모 커밋 위로 재배치할 때, 기존 커밋의 해시는 변경된다.

이는 결국, commit이 diff(변경 사항)을 포함하고 있다는 사실과 깊이 관련되어 있다.

 

 

 

⛺️ rebase를 "베이스캠프 이동"에 비교해 보자!

 

예를 들어, feature 브랜치를 main 브랜치에서 분기했다고 가정해 보자.

 

feature 브랜치의 모든 작업은 "베이스캠프"에서 출발한 것으로, main 브랜치의 특정 커밋(분기 시점)이 feature 브랜치의 "베이스캠프"가 된다.

feature 브랜치의 시작점을 main 브랜치의 새로운 커밋으로 옮겨서, 마치 feature 브랜치의 작업이 이 새로운 "베이스캠프"에서 시작된 것처럼 만드는 것이다.

 

좀 더 쉽게 말하자면, 기존 베이스캠프에서 작업한 흔적들이 보존되지만, 그 작업이 새로운 베이스캠프에서 시작된 것처럼 보이게 된다. 는 말과 같다.

 

이렇게 rebase를 "베이스캠프"를 다른 곳으로 옮기는 작업에 비유할 수 있다.

 

실제로 rebase 작업에서, 현재 브랜치의 커밋들을 다른 브랜치 위로 다시 쌓을 때 그 브랜치의 시작점이 되는 커밋이 "base"다.

(앞서 merge 작업 때 "base"를 기준으로 병합된다고 했다. 그 "base"와 같은 말이다!)

 

 


 

🕊️ git merge와 git rebase의 차이

 

merge 작업은 두 브랜치의 커밋 이력을 통합하는(합치는) 것! 이때 병합 커밋이 생성되며, 가지(branch)가 남게 된다. 

 

rebase 작업은 커밋 이력을 다른 브랜치의 커밋 이력에 재배치하여 깔끔한 히스토리를 만든다.

하지만 기존 커밋의 해시가 변경되므로, 협업 중일 때는 주의가 필요하다!