코드 저장소

공부에는 끝이 없다!

Git

Git - Merge와 Conflict

VarcharC2K 2023. 12. 29. 14:39

앞서서 Branch를 나누어 서로 줄기에서 다른 사용자가 개발을 동시에 진행하는 것을 수행해 보았다.

이전 글에서도 말했지만 Branch를 사용하는 이유는 보통 새로운 기능 개발을 위해 따로 떼어내기 위함이다.

그런데 생각해보면 기능 개발이 끝나면 결국 원래의 코드와 새로 만든 코드를 합쳐 하나의 코드로 만들어주어야 할 것이다.

이것을 가능하게 해주는 것이 Merge라는 명령이다.


Merge란?

Merge는 앞서 설명했든 나누어진 Branch를 하나로 합쳐주는 명령이다.

다음 이미지를 보면서 Merge를 진행하는 과정을 살펴보자.

 

우리는 어떤 기능을 개발하기 위해서 C1의 Commit 시점부터 m1 Branch를 만들어 기능 개발을 수행했다.

이 과정에서 C3~ C5까지의 Commit이 m1 Branch에서 수행되었고 기능 개발을 하는 동안 다른 개발자가 main에서 C2라는 작업을 수행했다고 가정해보자.

이제 우리는 기능 개발을 완료하였고 완성된 m1 Branch의 코드를 main Branch로 병합하여 다시 하나로 관리하고자 한다.

 

먼저, 병합을 진행하기 위하여 base가 될 Branch로 Head를 이동해 주어야 한다.

(현재는 *표시가 되어있는 m1에 Head가 있는 상태이다.)

 

*주의할 점이 있는데 Base가 된다는 것은 해당 Branch에 다른 Branch를 합치라는 뜻이다.

예를 들어, 우리는 main Branch에 m1 Branch를 합치는 것이므로 main으로 checkout 하여 base를 잡아준 것이다.

이것을 거꾸로 하면 main Branch가 m1에 병합되는 것이 된다.

(이 개념을 헷갈리면 나중에 Branch 관리시 골치아픈 상황이 발생할 수도 있다...)

 

 

Checkout 명령을 통해 base가 될 Branch로 왔다면 병합할 Branch를 Merge 명령어를 통해 합쳐준다.

명령어는 다음과 같다.

git merge [합칠 Branch 명]

 

 

명령을 수행하면 두개의 Branch가 합쳐지고 합쳐진 Commit C6가 생성되며 Merge가 종료된다.


소스트리를 이용하여 Merge 작업 수행하기

그럼 실제로 소스트리를 이용하여 Merge 작업을 수행해 보자.

 

현재 우리는 Master와 NewFunc라는 Branch가 있으며 NewFunc의 작업이 끝나 master로 병합을 진행한다고 가정한다.

앞서 설명했듯이 Merge를 진행하기 전 우리는 Base가 될 Branch로 이동하여야 한다.

따라서 소스트리 옆의 브랜치 탭에서 master를 더블 클릭하거나, 우클릭 > 체크아웃을 통하여 해당 Branch로 이동하여야 한다.

 

정상적으로 이동이 끝나면 해당 Branch 명이 굵게 강조되어 있을것이다.

굵게 강조되어 있다면 Checkout이 잘 된것이다.

 

그 후, 히스토리에서 원하는 Branch를 오른쪽 클릭하고 병합을 누른다.

 

그러면 병합확정 창이 나타나게 되는데 여기서 확인을 누르면 병합이 완료된다.

 

병합이 완료되면 master Branch와 NewFunc Branch가 모두 같은 위치에 있는 것이 확인 될 것이다.

 

보면 Push에 새로운 변경상황이 감지되고 로컬 master태그가 위로 올라간 것이 보일것이다.

origin/master는 원격저장소의 상태로 원격 저장소에서는 아직 병합이 진행되지 않았기 때문에 Push를 통하여 병합한 것을 원격 저장소로 올려 주어야한다.

 

 

Push를 진행할때는 우리는 master로 합쳐 준 것이기 때문에 master Branch에만 체크를 하고 Push를 진행하면 되겠다.

 

모든 작업이 종료되면 다음과 같이 모든 Branch가 같은 위치에 있는 것을 확인 할 수 있다.


Conflict - 고통의 시작...

병합을 시작하게 되면 앞으로 자주 보게 될 단어가 있다.

흔히 충돌났다 라고 말하는 Conflict는 Git을 잘 모르는 초보자 입장에서는 그야말로 재앙이나 다름없다.

특히나 Commit하는 걸 잊어버리고 작업하다가 올리려고 보니 충돌이 나서 처음부터 작업해야하나 고민했던 경험을 아마 초보자때는 다들 한번쯤 해봤을 것이다. 

그럼 병합시 충돌은 대체 왜 발생하는걸까?

이것을 알기 위해서 병합 방식에 대해서 알아야 할 필요성이 있다.

Git은 크게 2가지 병합 방법을 사용하는데

  1. Fast-Forward 방식
  2. 병합 Commit 생성방식

이렇게 2가지 방식이 있다.

Fast-Foward 과 병합 Commit 생성방식의 가장 큰 차이는 Base가 되는 Branch에 변화가 있느냐 에서 나온다.

도식을 보면서 예를 들어보자.

 

1.Fast-Foward 방식

 

우리는 C1에서 m1이라는 Branch를 새로 만들어 특정 기능을 개발하고자 한다.

따라서 기능 개발을 수행함에 따라 Commit 히스토리는 다음과 같이 쌓이게 될 것이다.

C1까지는 main으로 작업을 하였고 C2, C3는 m1 Branch에서 작업한 상태이다.

이 상태에서 main으로 m1을 병합하면 어떻게 될까?

 

main Branch는 변동사항이 없었기 때문에 최초로 갈라졌던 C1부터 C3까지 그냥 내려오기만 하면 된다.

즉, 단순하게 커밋 이동을 한것처럼 보여지게 된다.

Fast-Forward (빨리감기) 라고 부르는 것도 이런 이유 때문이며 사실상 main Branch에서 같은 Commit을 여러번 하는 것과 같기 때문에 충돌이 날 일도 없다.

 

2.병합 Commit 생성 방식

자, 그러면 병합 Commit을 생성하는 방식을 생각해 보자.

방식은 아까와 동일하다.

우리는 C1에서 m1 Branch를 생성하고 기능 개발을 하여 C3인 시점에서 병합을 진행하고자 한다.

그런데 여기서 아까와 다르게 실제 main Branch의 기능 변경이 필요하여 main에서 새로운 Commit을 진행하였다.

 

이 상태에서 병합을 진행하면 어떻게 될까?

 

아까와는 달리 m1의 위치로 main이 따라가는 것이 아닌, C5라는 병합 Commit을 생성하고 main이 위치하게 된다.

그리고 바로 여기서 충돌이 발생하게 된다.

 

왜 C5라는 병합 Commit이 필요할까?

그 이유는 m1이 이전에 main에서 수행되었던 C4라는 Commit을 알수 없기 때문이다.

즉, m1이 이전에 수행하였던 C2라는 과거와 다른 분기가 생겨버렸기 때문에 C4까지의 코드와 C3의 코드를 서로 비교하고 합쳐서 하나로 만드는 작업이 필요하고, 완료된 작업을 저장하기 위해서 C5라는 병합용 Commit이 필요해 지는 것이다.

 

그러면 충돌은 언제 발생할까?

예를 들어서, C3와 C4가 app.js라는 파일을 동시에 수정했다고 가정해보자.

C3에서는 num이라는 변수의 값을 1로 고정을 하였고 C4에서는 5로 고정하였다.

C3의 코드
{
	int num = 1
}
    
C4의 코드
{
	int num = 5
}

 

이 상태에서 병합을 진행한다고 할때, num의 값은 몇이 되어야 할까?

이것을 Git은 결정할 수 없다.

어떤 것이 진짜 쓰일 코드일지 Git이 알 수 없기 때문이다.

충돌이란 이런 선택 불가한 코드들에 대해서 발생하며 충돌이 발생하면 Git에서는 개발자의 선택을 위해 base에서의 코드와 병합하려는 compare의 코드를 모두 보여주며 이부분은 개발자가 선택하여 수동으로 고쳐야한다.

위의 코드를 병합시키면 다음과 같은 충돌이 나타날 것이다.

{
<<<< HEAD
	int num = 5
====
	int num = 1
>>>> m1
}

 

Head는 base Branch의 코드를, m1은 Compare쪽의 코드를 보여준다.

지금이야 예시로 굉장히 단적인 것을 들어서 간단해 보이지만 나중에 수십,수백 줄이 넘는 코드끼리 충돌나면 정말 복잡해진다.

 

그럼 다음에는 실제로 충돌을 발생시켜 보고, 발생한 충돌을 어떻게 해결하는지 직접 수행해 보도록 하자.

'Git' 카테고리의 다른 글

Git - Fork : 저장소 복제하기  (1) 2024.01.02
Git - 충돌과 충돌 해결 실습해보기  (1) 2023.12.30
GIT - Branch 나누기  (2) 2023.12.29
GIT - 소스트리 이용하기  (1) 2023.12.28
Git - GUI 이용하기  (0) 2023.12.27