본문 바로가기
야놀자 테크스쿨/TIL 및 문제정의, 시도, 해결법, 알게된 것

[Git] git 구조에 대해서 알아보자!

by 노잼인간이라불립니다 2023. 7. 15.

0. 개요

 git은 어떻게 버전관리를 하는지 git을 학습하면서 궁금해졌고, Git에 관련된 강의를 들으면서 Git이 어떻게 버전관리를 하는가에 대해서 마침 개념설명을 들었기에 머릿속에 좀 더 오래 남겨두기 위해 이 글을 작성한다.

 

1. Working Derectory - Index - Repository 구조.

 일단 깃의 원리를 알기 전에 필수로 알아야 하는 것이 있다. 바로 Working Directory(work tree), Index(Staging Area), Repository이다.

 아래 그림을 보자.

 최초에 "git init"을 입력하게 되면 .git 파일이 생기면서 해당 디렉토리는 Working Directory가 되고 Index와 Repository라는 가상 저장공간이 생긴다. (가상공간은 어떻게 구현되어 있을 걸까?)

 이렇게 실제 공간과 가상의 공간이 생긴 이후에 버전관리를 하기위해 git 명령어들을 이용하여 파일들을 저장하게 되면, 물리적으로는 그 정보들이 .git파일에 저장이 되어 버전관리가 이루어지게 된다.

 나는 이 과정에서 .git 파일이 각 명령어가 입력될 때 마다 어떻게 변하는지 그리고 파일을 어떤식으로 저장하고 Validation을 하는지 궁금해서 이 글을 작성하게 되었다. (가상공간이 구현되어 있는 .git파일을 파헤쳐 볼 것이다!)

 

2. 백문이 불여일견! 실제 눈으로 확인 해 보았다.

 1) 실제 눈으로 확인해보기 위해서는 gistory라는 python 라이브러리가 필요하다. 그렇기 때문에 나는 anaconda를 설치하고 python 3.8버전의 test라는 가상환경을 만들어 gistory 패키지를 설치했다.

 

 2) 그리고 gistory_test라는 폴더를 만들어 git init과 a.txt라는 파일을 새로 만들고 최초의 커밋을 발생시켰다.

 

 3) 그리고 gistory를 이용해서 .git 파일 내부를 살펴보았다.

 !! 여기서 중요하게 봐야하는 것은 .git/objects 와 .git/index 이다!! 머릿속에 담고 계속해서 살펴보자!

 

 

먼저 .git/objects 안에는 총 3가지 종류 객체들이 들어갔다.

첫 번째로, 파일의 내용을 나타내는 blob객체.

두 번째로, 디렉토리를 나타내는 tree 객체.

그리고 마지막으로 작성자, 커밋메시지 등을 담고있는 commit 객체.

이렇게 총 3가지가 objects 하위로 들어간 것을 볼 수 있다.

이들은 각각 add명령어를 입력했을 때, 그리고 commit 명령어를 입력했을 때 생성되었을 것이다!

이 부분은 뒤에서 다시 살펴 보도록 하자!!

 

정리하자면 objects하위에는 blob(파일 내용), tree(디렉토리), commit(커밋메시지 등) 객체가 들어갈 수 있다.

그리고 각 객체는 해시키를 가지고 있는 그것을 식별자로 사용하는 것 처럼 보인다.

그리고 이어서 Index 영역을 살펴보면??

Index 영역에서는 objects 안에있는 blob의 해시코드를 가지고 있는걸 볼 수 있다. 

 

아마 예상컨데 git add 명령어를 사용했을 때 Working Directory로 부터 blob 객체 생성되어 objects와 Index에 삽입 되었다고 생각해 볼 수 있겠다.

 

즉 .git파일은 글의 초반부에서 이야기 했던 Index와 Repository의 가상 저장소를 구현한 파일이라고 할 수 있겠다.

 

 

위의 가설을 좀 더 검증해보자!!

Working Directory에  b.txt파일을 새로 만들었음에도 불구하고, objects 하위에는 아무것도 생성되지 않았다.

 

 

 

git add명령어를 입력하자마자 .git파일의 인덱스와 objects 하위에는 b.txt에 대한 내용이 생성되었다.

 

 

즉, 정리하자면 git add 명령어를 쳐야만 .git파일 내부의 인덱스영역에 새로운 객체들이 생성된다.

 

그리고 또 한가지 새로운 사실은 커밋객체는 현재 tree(working directory)에 있는 모든 파일의 해시 정보를 가지고 있다!

즉 commit을 할 때 마다 그 버전의 디렉토리에 있는 모든 파일이 저장된다. 즉 스냅샷을 뜨게 된다.

그리고 2번째 커밋객체부터는 그 이전 커밋의 해시값인 parent객체도 가지고 있다. 이 또한 이전 커밋객체의 해시값이다

 

정리하자면 commit이 발생하면 현재 버전의 snapshot이 생성된다!!! (아 이래서 버전관리가 되는 거구나~)

 

 

마지막으로 a.txt 파일을 하나는 하위폴더에 이름을 같게 해서 만들고, 하나는 이름을 다르게해서 만들고 git add 명령어를 입력했는데 ~~ 결과는 두구두구~~

 

같은 파일을 복사하여 만든 파일들이 모두 같은 해시를 가지고 있는 것을 확인해 볼 수 있다.

즉, objects 하위에 저장될 때 중복된 파일을 저장을 하지 않고, 인덱스에서 필요할 경우 해시값을 참조해서 쓴다는 것을 알 수 있다.

이렇게 되면 같은 파일이 만개 십만개 늘어나도 하나의 파일로 처리를 할 수 있으니 성능적으로는 매우 뛰어날 것으로 예상된다!!

 

 

4. 정리 및 알게 된 것.

.git파일에 어떤 식으로 우리의 파일이 저장되는지 궁금했었는데 궁금증이 해소되는 시간이 었다.

 

마지막으로 핵심만 정리해보자면, 

 

1. git add 명령어를 입력하면, index와 objects에 올라간다.

2. git commit 명령어를 입력하면 objects 하위에 commit 객체가 새로 생성 된다.

3. objects 하위에는

    1) 파일의 내용을 저장한 blob,

    2) 디렉토리와 하위 파일 정보를 가지고 있는 tree,

    3) commit 메시지 등을 가지고 있는 commit 객체가 들어갈 수 있다.

4. commit이 발생할 때마다 snapshot이 발생한다.

 

그리고 스테이징 여부와 commit 여부를 확인하는 것도 결국에는 Working Directory와 objects, Index등을 비교하여 확인하는 것이라고 깨닫게 되었다.

 

시간은 조금 걸렸지만 .git파일에 대한 궁금증을 해소 할 수 있는 좋은 기회였다.

 

참고 

유튜브 생활코딩 강좌 - 지옥에서온 git