Git은 내용이 주소를 가지는 파일시스템이다. HashMap 구조를 생각했을 때, 내용은 value가 되고 주소는 key가 될 수 있다. 그리고 key를 통해 내용을 찾을 수 있다.
git init 명령을 통해 저장소를 초기화하면 우선 .git 디렉터리가 생기고 그 아래에 objects 디렉터리를 만들고 또 그 아래에 pack이랑 info 디렉터리를 만든다.



git init을 실행하고 .git 디렉터리 내부 구조
주어지는 데이터를 저장하고 이 데이터에 접근하기 위한 key를 반환한다.
1️⃣ git 저장소에 데이터를 넣어보자
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
‘test content’라는 내용을 표준 입력으로 받아서 git hash-object 명령어를 통해 데이터를 저장했다.
이 때
-w 옵션이 없으면 데이터를 저장하지 않고 내용을 해시한 값인 key만 출력한다.2️⃣ 이제 저 데이터를 git은 어떻게 저장했을지 알아보자
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
git hash-object 명령을 수행하고 출력된 값은 40자 길이의 해시값이다. 이 때 SHA-1을 사용한다.
이 출력에서 앞 두 글자를 따서 디렉터리를 만들고 나머지 38글자로 파일을 만든다.
3️⃣ 그럼 이제 저장된 데이터를 key로 찾아보자
$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
git cat-file 명령에서 -p 옵션을 주면 파일 내용을 살펴볼 수 있다.

먼저 ‘version 1’이라는 내용을 저장해보자
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
그리고 ‘version 2’라는 내용을 저장해보자
$ echo 'version 2' > test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
이 결과로 test.txt 파일의 내용은 ‘version 2’가 된다.
.git/objects 아래 파일 종류인 파일을 찾아보면 다음 3개의 파일이 존재한다. 보면 test.txt 파일이 이전에 ‘version 1’이라는 내용을 가졌던 기록을 가지는 파일이 존재함을 알 수 있다.
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

이전 버전을 기록하고 있는 파일의 해시값을 통해서 이전 버전 내용을 불러올 수 있다.
또 저 해시값을 통해 파일을 수정할 수 있다. 이 때의 수정에 대한 버전 파일은 생기지 않는다.

파일 이름을 저장한다.
Inode나 일반 파일은 Blob 개체로 저장하고, 디렉터리는 Tree 개체로 저장한다.
Tree 개체는 여러 항목을 가질 수 있는데, Blob 개체, 하위 Tree 개체를 가리키는 SHA-1 포인터, 파일 모드, 개체 타입, 파일 이름이 있다. 그리고 파일 이름만 저장한 데이터도 Blob 개체에 포함된다.
main브랜치가 가리키는 Tree 개체를 살펴보자
일반적으로 blob 개체는 파일, tree 개체는 디렉터리이다.

Git은 일반적으로 Staging Area(Index)의 상태로 Tree 개체를 만든다.
그러므로 스테이징 영역에 파일을 추가해보자
$ git update-index --add --cacheinfo 100644 \
83baae61804e65cc73a7201a7252750c76066a30 test.txt
index는 스테이징 영역에 관한 정보를 담으므로 test.txt를 add 명령을 통해 스테이징 영역으로 올려주고 인덱스를 업데이트해주었다.
이제 스테이징 영역을 Tree 개체로 저장해보자. git write-tree
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 test.txt
그리고 이후에 스테이징 영역에 파일을 추가하고 변경된 스테이징 영역의 상태로 새로운 Tree 개체를 만들 수 있다.
Tree 개체를 하위 디렉토리로 추가하는 법
git read-tree 명령으로 Tree 개체를 읽어 스테이징 영역에 추가하고 Tree 개체를 만들면 Tree 개체가 하위 디렉토리로 들어간다.

| 파일 종류 | 파일 모드 |
|---|---|
| 일반 파일 | 100644 |
| 실행 파일 | 100755 |
| 심볼릭 링크 | 12000 |
파일 모드에서 앞 두 글자는 파일 타입을 가리키고 나머지 세 글자는 접근 권한을 나타낸다. 접근 권한 숫자 3자리를 계산하는 것은 익숙하니 파일 타입에 대해 알아보자.
| 파일 종류 | 파일 타입 |
|---|---|
| 일반 파일 | 10 |
| 심볼릭 링크 | 12 |
| 디렉터리 | 14 |
| 블록 디바이스 | 06 |
| 캐릭터 디바이스 | 02 |
| FIFO(named pipe) | 01 |
| 소켓 | 14 |
위 내용은 리눅스 헤더파일 <sys/stat.h>에 매크로로 정의되어 있다.
이렇게 버전별로 저장한 데이터에 대해 누가, 언제, 왜 저장했는지에 대한 정보를 커밋 개체에 저장한다.
커밋 개체 만들기 commit-tree
커밋 개체에 대한 설명과 Tree 개체의 SHA-1 값 한 개를 넘긴다.
$ echo 'first commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
커밋 개체의 내용을 확인해보자
$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
parent d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <schacon@gmail.com> 1243040974 -0700
committer Scott Chacon <schacon@gmail.com> 1243040974 -0700
first commit
커밋 개체를 더 만들면 각 커밋 개체는 이전 개체를 가리킨다. 그리고 커밋 개체에 parent 이전 커밋 개체가 포함된다.
$ echo 'second commit' | git commit-tree 0155eb -p fdf4fc3
cac0cab538b970a37ea1e769cbbde608743bc96d
$ echo 'third commit' | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9
git add - Git은 변경된 파일을 Blob 개체로 저장하고 현 Index에 따라서 Tree 개체를 만든다.
git commit - 이전 커밋 개체와 최상위 Tree 개체를 참고해서 커밋 개체를 만든다.

Git은 개체의 타입을 Blob으로 하고 공백 문자 하나, 내용의 크기, 마지막에 널 문자를 추가해서 헤더를 만든다.
blob #{content.length}\0
그리고 헤더와 원래 내용을 합쳐서 SHA-1 해싱을 통해 해시값을 얻는다.
Git은 zlib로 내용을 압축하고 이를 개체로 저장한다. SHA-1 해시값에서 앞의 두 글자를 디렉터리명, 나머지 38글자를 파일이름으로 사용하고 그 파일 안에 원래 내용을 넣는다.

index 파일의 내용과 커밋 개체가 가지는 tree 개체의 내용이 일치하는 것을 볼 수 있다.
레퍼런스
https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EB%82%B4%EB%B6%80-Git-%EA%B0%9C%EC%B2%B4