2019년 8월 13일 화요일

[GIT] 브랜치 (1)

   참고

   브랜치란 무엇인가

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

커밋하면 Git은 현 Staging Area에 있는 커밋 개체(커밋 Object)를 저장한다(이 때, 커밋 개체는 스냅샷의 포인터, 저자나 커밋 메시지 같은 메타 데이터, 이전 커밋에 대한 포인터 등을 포함한다.). 이전 커밋 포인터가 있어서 현재 커밋이 무엇을 기준으로 바뀌었는지를 알 수 있다.

파일이 3개 있는 디렉토리가 있고 이 파일을 Staging Area에 저장하고 커밋하는 예제를 살펴 보자. 파일을 Stage하면 Git 저장소에 파일을 저장하고(이것을 Blob이라고 부른다) Staging Area에 해당 파일의 체크섬을 저장한다.(checksum은 중복 검사의 한 형태)

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

이 작업을 끝내면 저장소에는 5 개의 데이터 개체가 생긴다. 각 파일에 대한 Blob 세 개, 파일과 디렉토리 구조가 들어 있는 트리 개체 하나, 메다 데이터와 루트 트리를 가리키는 포인터가 담긴 커밋 개체 하나이다.



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

새 브랜치 생성하기
testing 브랜치 생성, testing 브랜치를 가리키는 새로운 포인터 생성.
새로 만든 브랜치도 지금 작업하고 있던 마지막 커밋을 가리킴

$ git branch testing



Git은 현재 작업중인 브랜치가 무엇인지를 가리키는 포인터로 HEAD를 사용함.
HEAD 포인터와 현재 브랜치를 가리키는 포인터는 다른 포인터이므로 주의해야 함.

현재 브랜치(포인터들)가 어떤 커밋을 가리키는지 확인.

$ git log --oneline --decorate
f30ab (HEAD -> master, testing) add feature #32 - ability to add new formats to the central interface
34ac2 Fixed bug #1328 - stack overflow under certain conditions
98ca9 The initial commit of my project


*git log 명령어 옵션 조사해볼 것

브랜치 이동하기
checkout으로 HEAD가 가리키는 브랜치를 이동할 수 있음

$ git checkout testing


커밋을 새로 해보면

$ vim test.rb
$ git commit -a -m 'made a change'


testing 브랜치에서 작업을 하고 새로 커밋했기 때문에 testing 브랜치는 앞으로 이동했다. 하지만 master 브랜치는 여전히 이전 커밋을 가리킨다. master로 되돌아가보자.


$ git checkout master


이후 커밋을 하면 다른 브랜치의 자업들과 별개로 testing 브랜치에서 작업이 진행된다. 그래서 testing 브랜치에서 임시로 작업하고 master 브랜치로 돌아와서 하던 일을 계속할 수 있다.

   브랜치와 Merge의 기초
브랜치의 기초
아래와 같은 프로젝트가 존재한다고 가정하자.
그리고 이슈 관리 시스템에 등록된 53번 이슈를 처리한다고 하면 이 이슈에 집중할 브랜치를 만든다. 브랜치를 생성하면서 checkout까지 한번에 처리하려면 -b 옵션을 추가한다.

$ git checkout -b iss53
Switched to a new branch "iss53"

iss53 브랜치를 checkout했기 때문에(HEAD가 iss53을 가리킨다) 일을하고 커밋하면 iss53 브랜치가 앞으로 나간다.


다른 상황을 가정해보자. 프로젝트에 문제가 생겨서 즉시 고쳐야 하는 상황일 때, 버그를 해결한 Hotfix에 iss53이 섞이는 것을 방지하기 위해 iss53과 관련된 코드를 어딘가에 저장해두고 원래 운영 환경의 소스로 복구해야 한다면 master 브랜치로 돌아오면 된다.

하지만 브랜치를 이동하려면 워킹 디렉토리를 정리해야 한다. 아직 커밋하지 않은 파일이 checkout할 브랜치와 충돌나면 브랜치를 변경할 수 없기 때문이다. 지금 작업하던 것을 모두 커밋하고 master 브랜치로 옮긴다.

$ git checkout master
Switched to branch 'master'

그러면 워킹 디렉토리는 53이슈를 시작하기 이전 모습으로 되돌려진다.

핫픽스가 생겼을 때를 살펴보자. 'hotfix' 브랜치를 만들고 문제를 해결할 때까지 사용한다.


$ git checkout -b hotfix
$ vim index.html
$ git commit -a -m 'fixed the broken email address'



운영 환경에 적용하려면 문제를 제대로 코쳤는지 테스트하고 배포하기 위해 hotfix 브랜치를 master 브랜치에 합쳐야 한다. git merge 명령어를 사용하면 된다.


$ git checkout master
$ git merge hotfix

"fast-forward"는 hotfix 포인터가 가리키는 커밋이 master 포인터가 가리키는 커밋에 기반한 브랜치이기 때문에 브랜치 포인터는 merge과정 없이 그저 최신 커밋으로 이동하는 Merge 방식을 말한다.

문제를 해결했으면 다시 일하던 브랜치로 돌아가야 한다. 일하던 브랜치로 돌아가려면 hotfix를 master 브랜치에 적용하고 hotfix 브랜치가 필요없으면 hotfix 브랜치를 삭제한다.


$ git branch -d hotfix

이슈 53번을 처리하던 환경으로 되돌아가서 작업을 마무리 해보자.

git merge master 명령으로 master 브랜치를 iss53 브랜치에 Merge하면 iss53 브랜치에 hotfix가 적용된다. 아니면 iss53 브랜치가 master에 Merge할 수 있는 수준이 될 때까지 기다렸다가 Merge하면 hotfix와 iss53 브랜치가 합쳐진다.

Merge의 기초
53번 이슈를 해결하고 master 브랜치에 merge하는 과정을 살펴보자.


$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html |    1 +
1 file changed, 1 insertion(+)

hotfix와 달리 iss53은 현재 master 브랜치( 현재 브랜치가 가리키는 커밋)을 물려받은 것이 아니므로 'Fast-forward' Merge를 하지 않는다. 이 경우 Git은 각 브랜치가 가리키는 커밋들과 공통 조상을 사용하여 3-way Merge를 한다.


3-way Merge의 결과를 별도의 커밋으로 만들고 해당 브랜치가 그 커밋을 가리키도록 한다.


그 후, iss53 브랜치는 더이상 필요하지 않으므로 삭제하고 이슈의 상태를 처리 완료로 표시한다.

충돌의 기초
3-way Merge가 실패할 때도 있다. Merge하는 두 브랜치에서 같은 파일의 한 부분을 동시에 수정하고 Merge하면 Git은 해당 부분을 Merge하지 못한다. 그러면 Git은 Merge 하지 못하고 충돌(Conflict) 메시지를 출력한다.


$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Merge를 하지 못했으므로 새 커밋이 생기지 않는다. Merge 과정을 진행할 수 없다. 충돌이 일어났을 때, 어떤 파일을 Merge 할 수 없었는지 살펴보려면 git status를 이용한다.


$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

충돌이 일어난 파일은 unmerged로 표시된다.

충돌이 일어난 부분을 고치고 git add으로 저장한다.


$ git add index.html

또는 git mergetool 도구로 충돌을 해결할 수도 있다.

$ git mergetool

   브랜치 관리

브랜치 목록 보기
* 기호가 붙어 있는 브랜치는 현재 checkout해서 작업하는 브랜치를 가리킴.
-v 옵션은 브랜치별로 마지막 커밋 메시지도 보여줌

$ git branch
  iss53
* master
  testing
$ git branch -v
  iss53   93b412c fix javascript issue
* master  7a98805 Merge branch 'iss53'
  testing 782fd34 add scott to the author list in the readmes

Merge된 브랜치인지 아닌지 확인하기
* 기초가 붙어 있지 않은 브랜치는 다른 브랜치와 Merge했기 때문에 삭제해도 되는 브랜치를 가리킴.


$ git branch --merged
  iss53
* master

현재 checkout한 브랜치에 Merge 하지 않은 브랜치 보기


$ git branch --no-merged
  testing

댓글 없음:

댓글 쓰기