Git 개체

sith-call.dev·2025년 2월 9일
0

Git

목록 보기
2/3

Git은 Content-addressable 파일시스템이다.

Git의 핵심은 단순한 Key-Value(역주 - 예, 파일 이름과 파일 데이터) 데이터 저장소라는 것.

어떤 형식의 데이터라도 집어넣을 수 있고 해당 Key로 언제든지 데이터를 다시 가져올 수 있다.

Blob 개체

A Git blob (binary large object) is the object type used to store the contents of each file in a repository.

git hash-object 명령 (데이터 저장)

$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin [--literally]] [--] <file>git hash-object [-t <type>] [-w] --stdin-paths [--no-filters]

이 명령에 데이터를 주면 .git/objects 디렉토리(object 즉 개체 데이터베이스)에 저장하고 그 데이터에 접근할 수 있는 key를 알려주며 이 key는 저장소 내에서 유일하다.

-w 옵션을 줘야 실제로 저장한다. -w  가 없으면 저장하지 않고 key만 보여준다. 그리고 -stdin 옵션을 주면 표준입력으로 입력되는 데이터를 읽는다. 이 옵션이 없으면 파일 경로를 알려줘야 한다.

git hash-object 명령이 출력하는 것은 40자 길이의 체크섬 해시다.

Git은 데이터를 저장할 때 데이터와 헤더로 생성한 SHA-1 체크섬으로 파일 이름을 짓는다. 해시의 처음 두 글자를 따서 디렉토리 이름에 사용하고 나머지 38글자를 파일 이름에 사용한다.

git cat-file 명령 (데이터 불러오기)

$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
git cat-file (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) [--path=<path>] <object>
git cat-file (--batch[=<format>] | --batch-check[=<format>]) [ --textconv | --filters ] [--follow-symlinks]

git cat-file 명령에 -p 옵션을 주면 파일 내용이 출력된다.

앞에서와 같이 Git 데이터베이스에 개체를 저장하고 나면 이후에는 git cat-file 명령으로 저장한 데이터를 불러올 수 있다.

git cat-file -t 명령으로 SHA-1 key를 입력하면 가리키는 해당 개체가 무슨 개체인지 확인할 수 있다.

Tree 개체

A Git tree object creates the hierarchy between files in a Git repository. You can use the Git tree object to create the relationship between directories and the files they contain.

모든 것을 Tree와 Blob 개체로 저장한다. Tree는 유닉스의 디렉토리에 대응되고 Blob은 Inode나 일반 파일에 대응된다.

Tree 개체 하나는 항목을 여러 개 가질 수 있다. 그리고 그 항목에는 Blob 개체나 하위 Tree 개체를 가리키는 SHA-1 포인터, 파일 모드, 개체 타입, 파일 이름이 들어 있다.

Git은 일반적으로 Staging Area(Index)의 상태대로 Tree 개체를 만들고 기록한다.

git update-index 명령

.git/index(Staging Area)에 개체를 저장.

$ git update-index --add --cacheinfo 100644 \
  83baae61804e65cc73a7201a7252750c76066a30 test.txt
$ git update-index --add --cacheinfo <mode>,<sha1>,<path>

파일 모드(mode)

  1. 100644 : 보통의 파일
  2. 100755 : 실행 파일
  3. 120000 : 심볼릭 링크

git write-tree 명령

.git/index(Staging Area)에 저장된 개체들을 .git/objects에 Tree 개체로 저장.

$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30      test.txt
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree
git write-tree [--missing-ok] [--prefix=<prefix>/]
💡 git cat-file -t : 해당 blob 개체의 타입을 알 수 있다.

git read-tree 명령

처음에 만든 Tree 개체를 하위 디렉토리로 만들 수 있다.

git read-tree 명령으로 Tree 개체를 읽어 Staging Area에 추가한다.

-prefix 옵션을 주면 Tree 개체를 하위 디렉토리로 추가할 수 있다.

$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614
$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579      bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt
git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
		[-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
		(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])

Commit 개체

A Git commit is a snapshot of the hierarchy (Git tree) and the contents of the files (Git blob) in a Git repository.

commit 개체는 blob 개체, tree 개체에 대한 메타 데이터를 저장함.

commit-tree 명령

커밋 개체를 만드는 명령어.

이 명령에 커밋 개체에 대한 설명과 Tree 개체의 SHA-1 값 한 개를 넘긴다.

git commit-tree <tree> [(-p <parent>)]
git commit-tree [(-p <parent>)] [-S[<keyid>]] [(-m <message>)]
		  [(-F <file>)] <tree>

최초의 커밋

$ echo 'first commit' | git commit-tree d8329f
**fdf4fc3**344e67ab068f836878b6c4951e3b15f3d
$ git cat-file -p **fdf4fc3**
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <schacon@gmail.com> 1243040974 -0700
committer Scott Chacon <schacon@gmail.com> 1243040974 -0700

first commit

부모가 존재하는 커밋

$ echo 'second commit' | git commit-tree 0155eb -p **fdf4fc3**
***cac0cab***538b970a37ea1e769cbbde608743bc96d
$ echo 'third commit'  | git commit-tree 3c4e9c -p ***cac0cab***
1a410efbd13591db07496601ebc7a059dd55cfe9

commit 개체의 형식

해당 스냅샷에서 최상단 Tree를(역주 - 루트 디렉토리 같은) 하나 가리킨다. 그리고 user.name 과 user.email 설정에서 가져온 Author/Committer 정보, 시간 정보, 그리고 한 라인 띄운 다음 커밋 메시지가 들어간다.

<Commit 개체의 형식>
type SHA-1 값
Author : {이름} <이메일>
Date : 날짜

	commit 메세지
diff 내용

git log 명령

Shows the commit logs.

git log [<options>] [<revision range>] [[--] <path>]

위에 제시된 커밋들을 git log로 보면 아래와 같다.

부모와 자식 간의 연결고리에 주의할 것.

$ git log --stat 1a410e
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:15:24 2009 -0700

    third commit

 bak/test.txt | 1 +
 1 file changed, 1 insertion(+)

commit cac0cab538b970a37ea1e769cbbde608743bc96d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:14:29 2009 -0700

    second commit

 new.txt  | 1 +
 test.txt | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:09:34 2009 -0700

    first commit

 test.txt | 1 +
 1 file changed, 1 insertion(+)

Git은 변경된 파일을 Blob 개체로 저장하고 현 Index에 따라서 Tree 개체를 만든다. 그리고 이전 커밋 개체와 최상위 Tree 개체를 참고해서 커밋 개체를 만든다.

즉, 커밋에는 옵션을 이용해서 부모를 설정해줄 수 있다.

중간 요약

Git은 변경된 파일을 Blob 개체로 저장하고 현 Index에 따라서 Tree 개체를 만든다. 그리고 이전 커밋 개체와 최상위 Tree 개체를 참고해서 커밋 개체를 만든다. 즉 Blob, Tree, 커밋 개체가 Git의 주요 개체이고 이 개체는 전부 .git/objects 디렉토리에 저장된다.

$ find .git/objects -type f
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
.git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
.git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1

개체 저장소

blob 개체가 저장되는 순서

Git은 개체의 타입을 Blob으로 만들면서 이를 시작으로 헤더를 만든다.

그 다음에 공백 문자 하나, 내용의 크기, 마지막에 널 문자를 추가한다.

Git은 헤더와 원래 내용을 합쳐서 SHA-1 체크섬을 계산한다.

Git은 또 zlib으로 내용을 압축한다.

마지막으로 zlib으로 압축한 내용을 개체로 저장한다.

SHA-1 값 중에서 맨 앞에 있는 두 자를 가져다 하위 디렉토리 이름으로 사용하고 나머지 38자를 그 디렉토리 안에 있는 파일이름으로 사용한다.

Git 개체는 모두 이 방식으로 저장하며 단지 종류만 다르다.

Ruby 코드로 저장되는 순서 보기

$ irb
>> content = "what is up, doc?"
=> "what is up, doc?"

>> header = "blob #{content.length}\0"
=> "blob 16\u0000"

>> store = header + content
=> "blob 16\u0000what is up, doc?"
>> require 'digest/sha1'
=> true
>> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37"

$ echo -n "what is up, doc?" | git hash-object --stdin
bd9dbf5aae1a3862dd1526723246b20206e5fc37

>> require 'zlib'
=> true
>> zlib_content = Zlib::Deflate.deflate(store)
=> "x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\a\x00_\x1C\a\x9D"

>> require 'zlib'
=> true
>> zlib_content = Zlib::Deflate.deflate(store)
=> "x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\a\x00_\x1C\a\x9D"

개체가 저장되는 과정 요약

  1. 헤더(header)를 만든다.
    1. 헤더 = 개체의 타입 + 공백문자 한 개 + 내용의 크기 + 널 문자
  2. 헤더와 원래 개체의 내용(content)을 합친다.
    1. store = header + content
  3. 합친 내용(store)을 SHA-1 체크섬 계산을 한다.
    1. store의 해시값을 구한다.
    2. hashed_value = hash_SHA-1(store)
  4. 합친 내용을 zlib으로 압축한다.
    1. zlib_content = zlib(store)
  5. 압축한 내용(zlib_store)을 정해진 장소에 저장한다.
    1. hashed_value의 첫 두 글자를 이름으로 하는 디렉토리를 만든다.
    2. 해당 디렉토리 안에 나머지 해시값을 이름으로 하는 파일을 만든다.
    3. 그 파일 안에 zlib_content를 저장한다.
profile
lim (time → ∞) Life(time) = LOVE

0개의 댓글

관련 채용 정보