슬기로운 개발생활

[Git] Git 버전관리 - 기본(branch, checkout, merge, conflict)

by coco3o
반응형

branch란?

git branch 명령어를 알기 전에 branch의 의미를 알아보자.

네이버 영어사전을 기준으로 branch는 나뭇가지, 지사 또는 (둘 이상으로)갈라지다라는 뜻을 가지고 있다.

먼저 branch의 메커니즘을 알기보다 간단한 사례를 통하여 알아보자.

어떠한 보고서를 작성해야 하는데 개인이 아닌 팀 프로젝트라고 가정해보자.

그리고 팀플 주제가 "개발자에 관하여"라 하고, 개발자의 전반적인 내용을 담은 보고서를 작성하였다. 이 문서는 개발자의 종류, 전망, 연봉 등 무슨 내용이 들어갈지 틀을 잡았다고 생각하자.

이 문서를 가지고, 위에서 잡아 놓은 종류, 전망, 연봉에 대한 내용을 아래와 같이 따로 따로 작성할 수 있다.

이렇게 공통 보고서로부터 2개의 가지가 생겼다. 그리고 공통 보고서에서도 버전 관리를 할 수 있고, 종류와 전망 보고서에서도 버전 관리를 독자적으로 할 수 있다.

또한, 마지막엔 종류와 전망에 대한 보고서를 공통 보고서로 합칠 수도 있다. 이를 병합이라고 한다.

즉, git에서 branch는 만들어 놓은 버전의 복사본을 새롭게 생성하여 작업을 이어나가는 것으로 이해하면 될 것이다.


1. branch 확인하기
f1.txt 파일을 하나 만들어서 내용은 a라 하고 add와 commit을 한다. 여기서 commit 메시지는 ver.1로 한다.

그 다음 git branch를 입력해 본다.

현재 git의 branch 목록을 보여주는 명령어이다. 처음 init을 하게 되면 master라는 기본 branch를 생성해준다.
그리고 master의 왼쪽에 * 표시가 있는데, 이것은 현재 사용중인 branch를 뜻한다.



2. 새 branch 만들기
git branch (브랜치 명)을 입력하면 새 branch를 만들 수 있다.

위와 같이 exp 브랜치가 생겼음을 알 수 있다.



3. 다른 branch로 전환하기
이제 master 브랜치에서 exp 브랜치로 전환해보자.

이때 사용되는 명령어는 checkout이다. git checkout (브랜치 명)

위와 같이 * 표시가 master에서 exp로 변경된걸 볼 수 있다.



4. branch 삭제하기
git branch -d (브랜치 명)을 사용하는데, 현재 사용중인 브랜치는 삭제할 수 없다.

master 브랜치로 전환한 후 exp 브랜치를 삭제해보자.

위와 같이 exp 브랜치가 삭제된 것을 볼 수 있다.

병합하지 않은 브랜치를 강제로 삭제할 땐 git branch -D 를 사용한다.



5. branch 생성과 전환동시에 하기
git checkout -b (브랜치 명)를 하면 브랜치를 생성과 전환을 동시에 할 수 있다.

 


branch의 특징

branch는 독자적인 버전 관리가 가능하다.

간단한 예제로 확인해보자. 우선 f2.txt와 f3.txt를 생성하고 내용은 각각 b와 c로 하고 add와 commit을 하는데,
commit 내용은 각각 ver.2와 ver.3으로 한다.

그리고 사용하는 브랜치를 exp로 바꾼 후 git log를 보자.

exp 브랜치는 "ver.1"에 해당하는 commit 메시지 정보만을 갖고 있다. master 브랜치에 있던 "ver.2"와 "ver.3"의 정보는 알 수 없다. 그 이유는 master 브랜치 내에서 독자적인 버전 관리를 수행했기 때문이다.

exp 브랜치에서도 f4.txt와 f5.txt를 생성하고 내용은 d와 e로한다. 이후 add와 commit을 하는데, 각각 commit 메시지는 "ver.4"와 "ver.5"로 설정한다.

다시 checkout을 하여 master 브랜치로 이동하고, git log를 입력하면 당연히 "ver.4"와 "ver.5"의 commit 메시지 정보는 없을 것이다.


branch의 정보 확인

위와 같이 각각의 브랜치 내에서 git log를 하면 해당 브랜치에서 commit 히스토리를 확인할 수 있다.
전체 브랜치의 commit 히스토리를 보려면 아래와 같이 git log --branches --decorate 명령어를 사용하면 된다.


(HEAD -> master)는 현재 checkout 되어있는 브랜치인 master 브랜치를 의미한다.

위를 보면 ver.1 ~ ver.3 까지는 master 브랜치의 히스토리이며, ver.1, ver.4, ver5는 exp 브랜치의 히스토리임을 뜻한다.

여기서 더욱 가독성을 높여주는 명령어 --graph 가 있다.

왼쪽을 보면 "ver.1" 메시지로부터 시작하여 2개의 가지가 생겼고, 독자적인 버전 관리를 하는 모습이 보인다.
여기서 더 간단히 압축해서 볼 수 있는 방법이 있다. --oneline 명령어를 추가해보자.

복잡한 브랜치 구조를 간단하게 볼 수 있게 되었다.


두 branch 간의 비교

위와 같이 브랜치 구조를 보고 싶을 수 있지만, 단순히 두 개의 브랜치만을 비교하고 싶을 수도 있다.

이럴 때 git log (브랜치 명)..(브랜치 명)을 사용한다.

master 브랜치에 없는 commit 두 개를 보여준다. 반대로 exp..master를 입력해보자.

exp 브랜치에 없는 commit 두 개를 보여준다.

만약 더 자세한 차이를 보고 싶다면 옵션 -p를 추가하면 어떤 파일이 추가, 삭제, 수정되었는지 확인할 수 있다.
다음은 master 브랜치의 각 commit마다 어떻게 수정되었는지를 보여준다.
git log -p

위와 같이 ver.2엔 b가 추가되었고, ver.3엔 c가 추가되었다는 것을 볼 수 있다.
확인 후 q로 나가준다.


branch 병합

branch는 독자적인 버전 관리를 수행한다.
그리고 이 branch는 다시 하나의 branch로 병합이 가능하다.
먼저, 이전까지 수행한 작업을 git log --branches --decorate --graph --oneline으로 확인해보자.

처음에 "ver.1"에 해당하는 commit 메시지가 전송되었었고, 이후에는 두 가지 branch로 나뉘었다.

master 브랜치는 ver.1, ver.2, ver.3를, exp 브랜치는 ver.1, ver.4, ver.5에 해당하는 히스토리 정보를 저장하고 있다.

이 때 git merge (브랜치 명)으로 병합하려고 할 수 있다.
무언가 병합을 하려고 한다면 대상이 두 개여야 하는데 git merge (브랜치 명)은 브랜치를 하나만 입력 받는다.그렇다면 현재 사용중인 브랜치가 자동으로 병합 대상이 된다는 것을 알 수 있다.
master 브랜치에서 git merge exp를 입력해보자.

다음과 같이, master 브랜치에는 존재하지 않았던 f4.txt와 f5.txt 가 생성되었음을 알 수 있다.
이것은 exp 브랜치에 있는 파일과 서로 합친 것이다.
git log --branches --decorate --graph --oneline을 사용하여 브랜치 구조를 살펴보자.

상단에 exp 브랜치와 병합되었다는 메시지와 함께 초록색 선으로 새롭게 이어진 것을 확인할 수 있다.

그리고 exp 브랜치로 전환한 후, git log를 보면 exp는 변화가 없다.
병합은 master 브랜치 입장에서 이루어진 것이므로, exp 브랜치에는 영향이 끼치지 않는 것이다.


branch 충돌(conflict)해결

만약 서로 협업하는 과정에서 branch를 만들어 각자 개발을 했다면, 언젠간 소스코드를 합쳐서 테스트도 해보고 시스템에 적용도 해보고 해야할 것이다. 그런 상황에서 브랜치를 합치는 것(병합)을 merge라 한다.

merge 과정에는 크게 두 가지 상황이 존재한다.
1. 서로 다른 파일을 수정했을 때
2. 서로 같은 파일을 수정했을 때

1번의 상황이라면 불편할 것이 없다.
A라는 사람이 C라는 파일을 수정했고 B라는 사람은 D라는 파일을 수정했을 때 merge 한다면 git이 자동으로 소스코드를 합쳐줄 것이다.(충돌없음)

2번의 상황은 또 다시 세부적으로 두 가지 경우로 나뉜다.
2-1). 서로 같은 파일을 수정했지만 수정한 부분이 다를 때
2-2). 서로 같은 파일을 수정하고 수정한 부분이 겹칠 때

2-1)의 상황은 A라는 사람이 X라는 파일의 1~200번째 줄까지 수정했고, B라는 사람이 X라는 파일의 400~600번째 줄까지 수정했다면 이 역시도 1번 상황과 마찬가지로 git이 자동으로 소스코드를 합쳐줄 것이다.

2-2)의 상황은 A라는 사람이 X라는 파일의 특정 메소드 f()의 특정 부분을 수정했다고 하고,
B라는 사람도 똑같이 X라는 파일의 특정 메소드 f()의 특정 부분을 수정했다면,
(최소 한줄이라도 같은 부분을 수정했다고 가정)
git은 자동으로 merge를 해줄 수 없는 상황이라고 사용자에게 알린다. 이런 상황을 충돌(conflict)이 일어났다고 한다.


2번의 상황을 master와 exp 브랜치로 예를 들어보자.

우선, master브랜치에서 common.txt라는 파일을 만들고, exp브랜치에 merge한다.

* merge 메시지 파악하기 *
1. fast-forward : 빨리감기라는 뜻으로 예를 들어 A브랜치에서 급하게 다른것을 만들어보려고 B브랜치를 생성하고 B브랜치는 여러번의 commit이 있었다.
이런 상황에서 A브랜치에서 B브랜치를 merge한다고 가정할 때, A브랜치에서 B브랜치를 분기해준 것외에 별 다른 commit이 없었으므로 A브랜치는 B브랜치만큼 그냥 "빨리감기"하면 되는 상황이다.
그래서 이런 상황일 때 별도의 commit log없이 B브랜치가 만든 최신의 commit log를 가리키게 만드는 것을 fast-forward라고 한다.
2. recursive strategy : 재귀적인 전략으로 예를 들어 A브랜치에서 B브랜치를 생성했다. 그 후 A브랜치도 commit이 여러번 있었고 B브랜치도 commit이 여러번 있었을 때 A브랜치에서 B브랜치를 merge한다.
이러면 fast-forward할 수 없고 git은 A브랜치와 B브랜치의 조상(Base) commit을 찾아 내부적으로 3way merge라는 방법을 이용하여 merge를 해준다.

위와 같이 정상적으로 exp 브랜치에 common.txt가 merge된 것을 볼 수 있다.
2-1)의 예를 직접 해보자.
master 브랜치에서 같은 파일에 function b() {} 를 추가해주고,
exp 브랜치에서 같은 파일에 function c() {} 를 추가해서 merge를 해보자.

각각 branch의 변경사항을 add, commit을 해주고, 아래와 같이 merge를 해주면 정상적으로 merge가 될 것이다.

아래는 master브랜치의 common.txt

exp브랜치로 checkout 후 master브랜치와 동일하게 merge를 해주었다.



2-2)의 충돌상황
먼저 master의 common.txt를 다음과 같이 수정하고 add commit을 진행한다.

그리고 exp의 common.txt를 master에서 수정한 곳과 동일한 곳에서 수정을 하고 add commit을 한다.

다시 master로 돌아와서 git merge exp 명령어를 실행하면 아래와 같이 충돌(conflict)이 발생한다.

이 상태에서 git status 명령어로 조회를 해보자.

merge에 실패했다고 뜬다. common.txt 파일을 열어보면 아래와 같이 바뀌어 있는 것을 볼 수 있다.

중간의 ===를 중심으로 위쪽의 <<<HEAD의 부분은 현재 checkout한 브랜치의 수정사항이고,
>>>exp의 부분은 말 그대로 exp 브랜치의 내용이라는 것이다.

우리는 위의 정보를 바탕으로 문제를 잘 해결해야 할 것이다.

위와 같이 수정 후 add를 해주고 다시 git status를 해보면 위와 다르게 바뀌어 있는 것을 볼 수 있다.

여기서 git commit 명령어를 입력하면 아래와 같이 충돌(conflict)이 났던 것을 수정했고, merge를 했다는 것을 볼 수 있다.

 

그리고 common.txt를 확인해보면 잘 처리가 된 것을 확인할 수 있다.




references : 생활코딩 지옥에서 온 git
https://steady-coding.tistory.com/283?category=899619
https://jeong-pro.tistory.com/106

반응형

블로그의 정보

슬기로운 개발생활

coco3o

활동하기