Pro Git의 Git Internals 챕터를 읽고 정리하였다. 코드는 로컬에서 직접 실행한 결과이다.
파일 내용을 byte로 바꾸어 저장한 파일이다.
hash-object -w은 Blob 개체를 생성한다.
-w 옵션을 주면 파일을 저장한다.디렉토리와 파일은 다음 규칙에 의해 생성된다.
# 기본 동작
$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
# 파일을 수정한 경우
$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30
$ echo 'version 2' > test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
헤더 + 내용의 구조를 만든다. 전체 구조는 <타입> <내용의 크기>\0<실제 파일 내용>이다.
→ 이 구조로 SHA-1을 계산한다. 이 값은 파일 저장 경로와 포인터로 사용한다.
→ 해당 구조를 zlib으로 압축하여 파일 내용으로 저장한다.
저장한 데이터는 일반적인 인코딩이 아니다. 읽기 위해서는 cat-file 명령을 사용해야 한다.
-p: 내용을 터미널에 출력-t: 해당 개체의 종류를 출력$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
파일이름과 해시에 대한 매핑을 저장하고 있는 개체이다.
모드 종류 해시 파일명
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README
100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib
Git은 일반적으로 Staging Area(Index)의 상태대로 Tree 개체를 만들고 기록한다. 그래서 우선 Staging Area에 파일을 추가해서 인덱스를 만들어야 한다.
update-index: 인덱스를 변경한다.write-tree: Staging Area의 내용을 Tree 개체로 저장한다.Tree 개체 내부에 다른 Tree 개체를 포함할 수 있다. 이는 별도의 디렉터리를 나타낸다.
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
위와 같은 Tree 개체가 있다면, 이는 루트 디렉터리에 bak 디렉터리, new.txt, test.txt가 있는 형태를 나타낸다.
스냅샷을 누가, 언제, 왜 저장했는지에 대한 정보는 Commit 개체에 저장된다.
commit-tree 명령을 통해 Commit 개체를 생성한다. 커밋 개체에 대한 설명과 Tree 개체의 SHA-1 값 한 개를 넘겨야 한다.
$ echo 'first commit' | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
196d012d42706c7076bf4041ff4409e11672770d
$ git cat-file -p 196d012d42706c7076bf4041ff4409e11672770d
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 # 최상단 Tree
author Jaehyeon Han <jaehyeonhan99@gmail.com> 1744938717 +0900
committer Jaehyeon Han <jaehyeonhan99@gmail.com> 1744938717 +0900
first commit # 커밋 메시지
commit-tree 명령의 -p 인자로 부모 커밋을 지정할 수 있다. 이렇게 연결된 Commit 개체들은 하나의 히스토리를 만든다.
위 내용이 git add 와 git commit 명령을 실행했을 때 Git 내부에서 일어나는 일이다. Git은 변경된 파일을 Blob 개체로 저장하고 현 Index에 따라서 Tree 개체를 만든다. 그리고 이전 커밋 개체와 최상위 Tree 개체를 참고해서 커밋 개체를 만든다. 즉 Blob, Tree, Commit 개체가 Git의 주요 개체이고 이 개체는 전부 .git/objects 디렉토리에 저장된다.
재밌는 점은 다른 Tree 개체가 같은 Blob 개체를 가리킬 수 있다는 점이다. 이 때문에 변경이 없는 파일은 별도의 Blob 개체가 생성되지 않아 저장공간을 아낄 수 있다.

각 개체의 SHA-1에 대한 별칭이라고 생각하면 된다. 브랜치의 역할이 정확히 이것이다.
update-ref <ref> <new_sha1> [<old_sha1>]를 통해 Ref를 만들거나 변경할 수 있다.
브랜치의 경우 refs/heads/브랜치 이름 경로에, 태그의 경우 refs/tag/태그 이름 경로에 저장한다.
HEAD 파일은 현 브랜치를 가리키는 간접(symbolic) Ref다. 간접 Refs라서 다른 Refs를 가리키고, SHA-1 값은 없다. 파일을 열어 보면 아래와 같이 생겼다.
cat .git/HEAD
ref: refs/heads/master
체크아웃이나 커밋을 하면 이 파일의 ref 값이 변경된다.
symbolic-ref <name> <ref>를 통해 name이 가리키는 ref를 변경할 수 있다.
태그 개체는 Commit 개체와 유사하게 작성자, 작성 시점, 태그 메시지, 가리키는 커밋이 포함된다.
태그 개체는 Tree 개체가 아니라 Commit 개체를 가리킨다. 하지만 브랜치와 달리 가리키는 커밋 개체를 바꿀 수는 없다.
참고로 Commit 개체뿐만 아니라 모든 Git 개체에 태그를 달 수 있다.
간단하게 커밋 개체의 SHA-1 값만 갖고 있다.
git update-ref refs/tags/1.0 196d012d42706c7076bf4041ff4409e11672770d
$ cat .git/refs/tags/1.0
196d012d42706c7076bf4041ff4409e11672770d
Annotated 태그는 내부에 부가 정보를 저장하기 때문에 update-ref만으로는 만들 수 없다. refs/tags/태그명 에 만들어진 파일에는 태그 개체의 SHA-1 값만 들어있고, 해당 개체에 태그 관련 정보가 담긴다.
$ git tag -a 1.1 fe76f45f5bc27d9f931cb7e20816a543d1b62374 -m 'test tag'
$ cat .git/refs/tags/1.1
0d428f64f35ae3ce7be711ddc282568a8dae4c50
$ git cat-file -p 0d428f64f35ae3ce7be711ddc282568a8dae4c50
object fe76f45f5bc27d9f931cb7e20816a543d1b62374
type commit
tag 1.1
tagger Jaehyeon Han <jaehyeonhan99@gmail.com> 1744944129 +0900
test tag
Git은 연결된 원격 브랜치의 정보를 refs/remotes/<리모트이름>/<브랜치이름> 경로에 저장한다. push나 fetch 시에 변경된다.