공사중

쉽게 정리한 Git 사용법 : 브랜치branch의 깊은 이해(checkout, HEAD, --graph --all) 본문

개발 | Git

쉽게 정리한 Git 사용법 : 브랜치branch의 깊은 이해(checkout, HEAD, --graph --all)

행운개발자 LuckyDeveloper 2019. 2. 21. 18:24

  브랜치를 설명할 떄는 원문에 있는 내용을 많이 차용하겠습니다. 정확하게 설명하고 싶기 때문입니다. 


모든 버전 관리 시스템은 브랜치를 지원합니다. 개발하다보면 코드를 여러 개로 복사해야하는 일이 많은데, 코드를 통째로 복사하고나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있습니다. 이렇게 독립적으로 개발하는 것이 브랜치입니다.


커밋하면 Git은 현 Stagin Area에 있는 데이터의 스냅샷에 대한 포인터, 저자나 커밋 메시지 같은 메타데이터, 이전 커밋에 대한 포인터 등을 포함하는 커밋 개체(커밋 object)를 저장합니다. 이전 커밋 포인터가 있어서 현재 커밋이 무엇을 기준으로 바뀌었는지를 알 수 있습니다. 최조 커밋을 제외한 나머지 커밋은 이전 커밋 포인터가 적어도 하나씩 있고 브랜치를 합친 Merge 커밋 같은 경우는 이전 커밋 포인터가 여러 개 있습니다. 


브랜치가 무엇인지 깊이 이해하기 위해서..파일이 3개 있는 디렉토리가 있고 여기서 이 파일을 Staging Area에 저장하고 커밋할 때 내부적으로 어떤 일이 일어나는지 보겠습니다. 그림 설명이 있으니 그림을 보면서 이해해보겠습니다. 스냅샷과 세가지 상태 포스팅에서는 설명하지 않았던 부분을 다룹니다. 조금 어렵습니다. 


먼저 Git 저장소가 어떤 곳인지 알아야합니다. Git으로 무엇을 하든 데이터는 이 곳에 저장이 됩니다. 즉, 우리가 진행하고 있는 프로젝트의 메타데이터(커밋을 한 저자, 커밋 내용 등)와 객체 데이터베이스를 저장합니다. 데이터가 어떤 형식으로 저장되는지는 아직 모르지만 적어도 객체를 저장하는 object디렉토리가 .git이라는 이름을 가진 Git저장소에 존재하는 것을 알 수 있습니다. 


Git은 데이터를 저장하기 전에 항상 체크썸을 구하고 그 체크썸으로 데이터를 Git 저장소에 저장합니다. 파일을 Stage 하면 Git 저장소에 파일을, 체크썸을 사용해서, 저장하고 (Git은 이 과정을 Blob이라고 부릅니다.) Staging Area에 해당 파일의 체크썸을 저장합니다. Staging Area는 Git 저장소에 있는 index라는 이름이 적혀있는 파일입니다.  Staging Area를 기술적인 용어로 index라고 부릅니다. (그래도 Staging Area라고 부르는 것이 의미를 명확하게 인지할 수 있어서 Staging Area라는 용어를 사용해서 설명해왔습니다.) 익히 알고 있듯이 곧 커밋할 파일에 대한 정보를 '체크썸을 사용해서' 저장하는 역할을 하는 파일입니다. 


Git이 사용하는 SHA-1해시를 사용해서 만든 체크썸은 다음과 같은 모습입니다.

24b9da6552252987aa493b52f8696cd6d3b00373


Git은 모든 것을 해시로 식별합니다.(해시와 체크썸의 차이점은 아직 몰라도 됩니다.) 파일을 이름으로 저장하는 것이 아니라 해당 파일의 해시로 저장하는 것입니다.


 아래는 언제든지 스냅샷을 만들 수 있는 이유에 입니다.

쉽게는 "스냅샷을 만들 때 필요한 모든 정보를 저장해놔서 만들 수 있다."입니다.ㅋㅋㅋㅋ

 git commit으로 커밋하면 먼저 루트 디렉토리와 각 하위 디렉토리의 트리 개체를(라는 것을) 체크썸과 함께 저장소에 저장합니다(한다고 합니다.). 그다음에 커밋 개체를 만들고 메타데이터와 루트 디렉토리 트리 개체를 가리키는 포인터 정보를 커밋 개체에 넣어 저장합니다. 이 작업을 마치고 나면 Git 저장소는 다섯 개의 데이터 개체가 생깁니다. 각 파일에 대한 Blob 세 개, 파일과 디렉토리 구조가 들어있는 트리 개체 하나, 메타데이터와 루트 트리를 가리키는 포인터가 담긴 개체 하나입니다. 


파일을  수정하고 커밋하면 이전 커밋이 어떤 내용이었는지에 대한 내용도 저장합니다. 아래 그림에서 스냅샷 A를 만든 후 차례로 B와 C를 만든 구조입니다.



다시 돌아와서 Git의 브랜치는 커밋 사이를 가볍게 이동할 수 있는 어떤 포인터 같은 것입니다. 기본적으로 Git은 Master브랜치를 만듭니다.  처음 커밋하면 master 브랜치가 생성된 커밋을 가리킵니다. 이후 커밋을 하면 브랜치는 자동으로 가장 마지막 커밋을 가리킵니다. master 브랜치도 딱히 특별한 것이 아닙니다. 브랜치의 개념을 사용하기 위해서 git init을 했을 때 최초의 브랜치를 만들어야하는데, 이 브랜치의 이름이 default 값으로 master라고 정의된 것 뿐입니다. 

위 그림은 아래와 같은 뜻입니다.

어떤 데이터를 작성하고 이를 snapshot A로 commit한다. 
이때 Git의 특성상 해시로 저장이 되며 이 값은 98ca9로 시작한다.
34ac2/Snpashot B와 f30ab/Snapshot C도 같은 과정으로 생성된다. 
커밋을 해서 커밋 객체가 생성될 때 master 브랜치는 가장 최근의 커밋을 가리키도록 이동한다. 
snapshot C까지 만든 시점에서 v1.0이라는 tag를 붙혔다.
현재 내가 master branch를 checkout 하고 있다는 뜻에서 HEAD는 master를 가리킨다.


이제 브랜치를 이해했으니 브랜치를 만드는 방법입니다.

git branch [새로 만들 브랜치 이름]

이렇게 만든 브랜치고 지금 작업하고 있던 마지막 커밋을 가리킵니다.


(Tag 사용하기에서 언급했던 방법을 사용하면 이전 Tag가 가리키는 브랜치를 만들 수 있습니다.)

git checkout -b [새로 만들 브랜치 이름] [지정하고 싶은 태그의 이름]


git branch testing

을 사용해서 testing 이라는 브랜치를 만들었을 때의 구조를 보겠습니다. 

testing이라는 브랜치를 만들었더니 가장 최근의 커밋을 가리키도록 생성됩니다.


위에서  설명했듯이 HEAD는 현재 사용자가 보고 있는(checkout한) 브랜치를 알려줍니다.

만약 testing branch로 checkout하면 아래와 같이 HEAD가 이동합니다.

이 상태에서 커밋을 하면 testing 브랜치는 앞으로 이동하지만 master 브랜치는 움직이지 않습니다.

이 상태에서 다시 git checkout master 를 하면 아래와 같은 모습으로 변합니다.

이 때는 두 가지 일이 진행됩니다.

1. master브랜치가 가리키는 커밋을 HEAD가 가리키게 합니다.

2. Working Directory의 파일도 그 상태의 시점으로 돌려놓습니다.

앞으로 커밋을 하면 다른 브랜치의 작업들과 별개로 진행되기 때문에 testing 브랜치에서 임시로 작업하고 원래 master 브랜치로 돌아와서 하던 일을 계속할 수 있습니다.  다시 강조합니다. 브랜치를 이동하면 Working directory의 파일이 바뀝니다.


다시 이 상태에서 파일을 수정하고 커밋하면 어떻게 되는지 보겠습니다.

vim test.txt  -> [내용 입력] -> [ESC] -> :wq(저장후 종료)
git commit -a -m "made test.txt file"

(-a 옵션은 모든 파일을 Staging Area에 올리고 commit 한다는 옵션입니다.)




각 박스는 커밋 객체를 의미하고, 글씨는 해시를 나타냅니다. 빨간 글씨는 브랜치 이름을 나타냅니다.



그리고 git log를 찍을 때 위와 같이 --graph 옵션으로 로그의 왼쪽에 그래프를 그릴 수 있습니다. --all옵션을 추가해야지 현재 HEAD가 가리키는 커밋의 히스토리뿐만 아니라 HEAD가 가리키지 않는 모든 커밋의 log를 보고 그래프를 그릴 수 있습니다.


그래프가 어떤 식으로 그려지는지 제가 이전에 안드로이드 프로젝트를 하면서 만들었던 commit을 보여드릴게요.


왼쪽의 그래프를 보시면 브랜치들이 갈라지고 다시 합쳐지는 과정을 보실 수 있습니다. 합쳐지는 과정인 Merge는 다음 포스팅에서 정리합니다.



git 정리가 꽤 걸리고 있네요..개강 전까지는 다 하겠죠?