Git 개체

앵우·2025년 7월 30일

git init

Git은 내용이 주소를 가지는 파일시스템이다. HashMap 구조를 생각했을 때, 내용은 value가 되고 주소는 key가 될 수 있다. 그리고 key를 통해 내용을 찾을 수 있다.

git init 명령을 통해 저장소를 초기화하면 우선 .git 디렉터리가 생기고 그 아래에 objects 디렉터리를 만들고 또 그 아래에 pack이랑 info 디렉터리를 만든다.




git init을 실행하고 .git 디렉터리 내부 구조

  • HEAD: 현재 브랜치를 가리키는 포인터
  • config: 저장소 설정값(사용자명, 리모트 등)
  • refs/: 브랜치 및 태그 정보
    • heads/와 tags/ 디렉터리가 있다.
  • objects/: 커밋, 트리, 파일 객체 저장
  • index: staging area 정보를 담는 파일

git hash-object

주어지는 데이터를 저장하고 이 데이터에 접근하기 위한 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 옵션을 주면 파일 내용을 살펴볼 수 있다.

💡 참고
파일을 만들었을 때 바로 objects에 파일 내용이 들어가는 것은 아니다. `git hash-object` 명령을 통해 데이터를 넣거나 git add를 하면 objects에 파일 내용이 들어간다.

git 저장소의 버전 관리 살펴보기

먼저 ‘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

이전 버전을 기록하고 있는 파일의 해시값을 통해서 이전 버전 내용을 불러올 수 있다.

또 저 해시값을 통해 파일을 수정할 수 있다. 이 때의 수정에 대한 버전 파일은 생기지 않는다.

Tree 개체

파일 이름을 저장한다.

Inode나 일반 파일은 Blob 개체로 저장하고, 디렉터리는 Tree 개체로 저장한다.

Tree 개체는 여러 항목을 가질 수 있는데, Blob 개체, 하위 Tree 개체를 가리키는 SHA-1 포인터, 파일 모드, 개체 타입, 파일 이름이 있다. 그리고 파일 이름만 저장한 데이터도 Blob 개체에 포함된다.

main브랜치가 가리키는 Tree 개체를 살펴보자

일반적으로 blob 개체는 파일, tree 개체는 디렉터리이다.

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이 개체를 저장하는 법

Git은 개체의 타입을 Blob으로 하고 공백 문자 하나, 내용의 크기, 마지막에 널 문자를 추가해서 헤더를 만든다.

blob #{content.length}\0

그리고 헤더와 원래 내용을 합쳐서 SHA-1 해싱을 통해 해시값을 얻는다.

Git은 zlib로 내용을 압축하고 이를 개체로 저장한다. SHA-1 해시값에서 앞의 두 글자를 디렉터리명, 나머지 38글자를 파일이름으로 사용하고 그 파일 안에 원래 내용을 넣는다.

💡 참고
커밋을 할 때 index 파일은 변하지 않는다. 커밋을 하면 staging area를 스냅샷을 떠서 tree 개체를 만들고 tree 개체를 가지고 커밋 개체를 만든다.

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

0개의 댓글