git

[책] Pro Git 2판, review [5] - branch

AI Coder 2021. 2. 15. 21:55

Git branch

 

개발을 하다 보면 코드를 여러 개로 복사해야 하는 일이 자주 생긴다. 코드를 통째로 복사하고 나서, 원래 코드와는 상관 없이 독립적으로 개발을 진행할 수 있는데, 이렇게 독립적으로 개발하는 것이 브랜치 이다. 

 

브랜치란 무엇인가?

GIt 이 브랜치를 다루는 과정을 이해하려면 우선 GIt이 데이터를 어떻게 저장하는지 알아야 한다. 

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

Commit 하면 Git은 현 Staging Area에 있는 데이터의 스냅샷에 대한 포인터, 저자나 커멧 메시지 같은 메타데이터, 이전 커밋에 대한 포인터 등을 포함하는 커밋 object를 저장한다. 

이전 commit 포인터가 있어서 현재 commit이 무엇을 기준으로 바뀌었는지를 알 수 있다.

최초 commit을 제외한 나머지 commit은 이전 commit 포인터가 적어도 하나씩 있고

브랜치를 합친 Merger커밋 같은 경우에는 이전 commit 포인터가 여러개 있다. 

 

Working directory에 a.txt, b.txt, c.txt 를 신규로 generating 해보자.

git add a.txt b.txt c.txt 로 3개의 file 들을 Staging Area에 저장한다.  (Git은 이것을 Blob 라고 부른다.)

git commit -m 'The initial commit of my project' 으로 Staging Area에 해당 파일의 체크섬을 저장한다. 

git commit 으로 commit 하면 먼저 루트 디렉토리와 각 하위 디렉토리의 트리 개체를 체크섬과 함께 저장소에 저장한다. 그 다음에 commit object 를 만들고 메타데이터와 루트 디렉토리 트리 개체를 가리키는 포인터 정보를 commit object에 넣어 저장한다. 그래서 필요하면 언제든지 스냅샷을 다시 만들 수 있다. 

 

이 작업을 마치고 나면 Git 저장소에는 5개의 data object가 생긴다.

1. a.txt 에 대한 Blob

2. b.txt 에 대한 Blob

3. c.txt 에 대한 Blob

4. 파일과 디렉토리 구조가 들어 있는 트리 Object 하나,

5. 메타데이터(i.e. commit message등)와 루트 트리를 가리키는 포인터가 담긴 Commit Object 한다. 

 

 

다시 파일을 수정하고 Commit 하면 이전 Commit 이 무엇인지도 저장한다. 

Git 의 브랜치는 Commit 사이를 가볍게 이동할 수 있는 어떤 포인터 같은 것이다. 

기본적으로 Git은 master 브랜치를 만든다. 처음 Commit 하면 이 master 브랜치가 생성된 commit을 가리킨다. 

이후 commit을 만들면 브랜치는 자동으로 가장 마지막 commit을 가리킨다. 

 

git branch testing 

git branch 로 testing branch 를 만든다. 새로 만든 브랜치도 지금 작업하고 있떤 마지막 commit을 가리킨다. 

 

지금 작업 중인 브랜치가 무엇인지 Git은 어떻게 파악할까. 다른 버전 관리 시스템과는 달리 Git은 'HEAD' 라는 특수한 포인터가 있다. 이 포인터는 지금 작업하는 로컬 브랜치를 가리킨다. 브랜치를 새로 만들었지만 , Git은 아직 master 브랜치를 가리키고 있다. gi branch 명령은 브랜치를 만들기만 하고 브랜치를 옮기지 않는다. 

 

git log --oneline --decorate

--decorate 옵션을 사용하면 쉽게 브랜치가 어떤 commit을 가리키는지도 확인 할 수 있다. 

master 와 testing 브랜치들은 30ecdf0 commit을 가리킨다. 

Head 는 master branch를 가리킨다. 

 

git checkout testing

testing 브랜치로 변경한다. 다시 말해서 HEAD가 testing브랜치를 가리키도록 변경 한다. 

c.txt 파일을 수정하고 commit을 해보자. 

git add c.txt

git commit -m 'c.txt has been modified'

git log --oneline --decorate

HEAD 는 testing 브랜치를 가리키고 있으며, testing 브랜치는 가장 최근 commit인 b901e2c를 가리키고 있다.

반면 master 브랜치는 그 앞전 브랜치인 30ecdf0 commit을 가리킨다.

 

git checkout master

방금 실행한 명령은이 한 일은 2가지 이다. 

1) master 브랜치가 가리키는 commit을 HEAD가 가리키게 하고 

2) working directory의 file도 그 시점으로 돌려 둔다. 

마지막 commit (b901e2c) 의 c.txt 파일 내용은 아래와 같이 2줄 이다. 

 

그 앞전 commit(30ecdf0)의 c.txt 파일 내용은 아래와 같이 1줄이다. Master branch로 돌아온 순간, working directory 안에 있는 c.txt 파일이 변경 되었다. 

 

git checkout testing 

HEAD 는 다시 testing 브랜치를 가리키고, testing 브랜치는 최신 commit 을 가리킨다. Working directory의 c.txt  역시 2줄로 돌아왔다. 

git checkout master

b.txt file을 수정한다 

git commit -a -m 'made other changes'

이렇게 하면 프로젝트 히스토리는 분리돼 진행된다. 우리는 브래치를 하나 만드러 그 브랜체에서 일을 좀 하고, 다시 원래 브랜치로 되돌아와서 다른 일을 했다. 두 작업 내용은 서로 독립적으로 각 브랜치에 존재한다. 

git log --oneline --decorate --graph -all

현재 브랜치가 가리키고 있는 히스토리가 무엇이고 어떻게 갈라져 나왔는지 보여 준다. 

실제로 Git의 브랜치는 어떤 한 커밋을 가리키는 40글자의 SHA-1 체크섬 파일에 불과하기 때문에 만들기도 쉽고 지우기도 쉽다. 새로 브랜치를 하나 만드는 것은 41바이트 크기의 파일을 하나 만드는 것에 불과하다. 

 

Merge

 

Commit 사이를 자유롭게 이동하다가 때가 되면 두 브랜치를 Merge 한다. 

실제 개발과정에서 겪을 만한 예제를 하나 살펴 보자. 

1. 작업중인 웹사이트가 있다. 

2. 새로운 이슈를 처리할 새 Branch를 하나 생성한다. 

3. 새로 만든 Branch에서 작업을 진행한다. 

이때 중요한 문제가 생겨서 그것을 해결하는 Hotfix를 먼저 만들어야 한다. 그러면 아래와 같이 할 수 있다. 

1. 새로운 이슈를 처리하기 이전의 운영(Production) 브랜치로 이동한다. 

2. Hotfix 브랜치를 새로 하나 생성한다. 

3. 수정한 Hotfix 테스트를 마치고 운영 브랜치로 Merge 한다. 

4. 다시 작업하던 브랜치로 옮겨 가서 하던 일을 진행한다. 

 

 

다음과 같은 작업을 진행 해 본다. 

현재 master branch 는 a99abd6 commit를 가리키고 있다. 

master branch 의 working directory 에 abc.txt 를 생성하고 그안에 "abc-master" 라고 한 줄을 넣고 save 한다. 

git add abc.txt

git commit -m "master abc.txt"

iss72 라는 branch를 생성하고 checkout 한다.

git branch iss72

git checkout iss72 

or 

git checkout -b iss72 (branch 생성 + checkout) 

master 와 iss72 모두 a99abd6 commit를 가리킨다. 

abc.txt 를 수정한다. ("iss72 add" 라고 한줄을 추가한다.)

git commit -a -m 'added abc.txt [issue 72]'

다른 상황을 가정해 보자. 만드는 사이트에 문제가 생겨서 즉시 고쳐야 한다. 버그를 해결한 Hotfix에 iss72이 섞이는 것을 방지하기 위해 iss72과 관련된 코드를 어딘가에 저장해두고 원래 운영환경 소스로 복구해야 한다. Git을 사용하면 이런 노력을 들일 필요 없이 그냥 master 브랜치로 돌아가면 된다. 

 

그렇지만, 브랜치를 이동하려면 해야 할 일이 있다. 아직 commit 하지 않은 파일이 checkout할 브랜치와 충돌나면 브랜치를 변경할 수 없다. 브랜치를 변경할 때는 working directory를 정리하는 것이 좋다. 

 

작업하던 것을 모두 정리하고, master branch로 옮긴다. 

git checkout master 

git checkout -b hotfix2

abc.txt를 수정한다.  ("hotfix2 add" 라고 한줄을 추가한다.)

git commit -a -m 'added abc.txt [hotfix2]'

hotfix 가 충분히 검증되었고, 해당 bug가 fix되었다면. hot fix 를 master 브랜치에 합쳐야 한다. 

 

git checkout master  //base 를 master 로 잡고 

git merge hotfix       //hotfix 를 땡겨 온다. 

Merge  메시지에서 "fast-forward" 라는 단어가 보인다. 

Master 브랜치는,  hotfix2 브랜치의 조상이기 때문에,  

브랜치(Master) 포인터는 merge 과정 없이 그저 최신 커밋으로 이동한다.

이런것을 'Fast forward' 라고 부른다. 

 

이제 더이상 필요없은 hotfix 브랜치를 삭제 한다. 

git branch -d hotfix

 

 

이제 iss72으로 돌아가서 하던일을 계속한다. 

git checkout iss72

abc.txt를 수정한다. ("finished the new footer [issue 72]" 한줄을 add)

git commit -a -m 'finished the new footer [issue 72]'

 

이제 iss72 이 다 완성 되었다고 가정한다. 

master에 iss72을 merge 한다. 

 

git checkout master

git merge iss72

Master(c26ca20) 과 iss72(e8ff948) 이 merge에 실패 했다. 

우선 iss72는 Master의 직계 후손이 아니기 때문에 Fast foward 가 적요되지 않는다. 

이 경우에는 Git은 각 브랜치가 가리키는 커밋 두 개와 공통 조상 하나를 사용하여 3-way Merge를 한다. 

3-way Merge를 별도의 커밋으로 만들고 나서 해당 브랜치가 그 커밋을 가리키도록 이동 시킨다 

그래서 이런 커밋은 부모가 여러개고 Merge 커밋이라 부른다. 

 

"CONFLICT (content: Merge conflict in abc.txt" 메시지가 보이는 것처럼

master 에서 abc.txt 는 다음 두줄을 가지고 있다.

abc-master hotfix2 added iss72 에서 abc.txt는 다음 3줄을 가지고 있다. abc-masteriss72 addedfinished the new footer [issue 72]

 

Git은 자동으로 Merge 하지 못해서 새 커밋이 생기지 않는다. 변경사항의 충돌을 개발자가 해결하지 않는한 Merge 과정을 진행할 수 없다. Merge 충돌이 일어났을 때 Git이 어떤 파일을 Merge할 수 없었는지 살펴보려면 git status 명령을 이용한다. 

충돌이 일어난 파일은 unmerged 상태로 표신된다. Git은 충돌이 난 부분을 표준 형식에 따라 표시해 준다. 그러면 개발자는 해당 부분을 수동으로 해결한다. 

아래와 같이 iss72 브랜치의 abc.txt 와 동일하게 내용을 변경한후 저장한다. 

git commit -a -m 'abc.txt has been modified in accordance with iss72'

0aa83a9 이 merge commit 이 된다.

 

'git' 카테고리의 다른 글

Pro Git - 브랜치 워크플로, 브랜치 관리, 브랜치 추적  (0) 2021.02.22
Git 필수 명령어 요약  (0) 2021.02.16
[책] Pro Git 2판, review [3]  (0) 2021.02.15
[책] Pro Git 2판, review [2]  (0) 2021.02.10
[책] Pro Git 2판, review [1]  (0) 2021.02.10