컴퓨터/Git

[Git] Git의 내부 구조 (Blob, Tree, Commit)

dobshn 2024. 9. 16. 23:08

Git 시스템의 Objects는 3가지이다.

- Blob

- Tree

- Commit

(Annotated tag라는 Object가 하나 더 존재하지만, 여기선 다루지 않는다.)

 

1. Blob

 

Blob은 Binary Large Object의 줄임말로, 어떤 파일의 압축된 데이터이다.

파일 시스템의 파일과 다르게, 메타데이터(작성 시간, 작성자 등의 정보)가 없다.

Blob의 크기는 원본 파일의 크기와 비례하는 경향이 있다.

 

Git 시스템에선 Objects를 SHA-1 해시를 통해 식별한다. SHA-1 해시는 파일의 내용을 기반으로 40Byte의 문자열을 생성한다.

그렇게 생성된 SHA-1 해시의 값으로 각 Objects를 식별한다.

 

git add

어떤 파일을 git add 명령을 통해 Stage 하면, 그 파일의 내용을 바탕으로 SHA-1 해시 값을 구하고, 이를 그 파일의 압축파일은 Blob의 식별자로 사용한다.

 

Staging Area에는 파일의 이름과 경로, 파일의 Blob 파일에 대한 참조가 올라간다.

 

2. Tree

Tree 객체는 디렉토리 구조를 나타낸다.

 

Tree 객체의 노드는 하나의 파일 혹은 또 다른 tree 객체이다.

 

Tree 객체에 파일이 저장될 때, 파일의 이름과 그 파일의 Blob 객체에 대한 참조(SHA-1 해시)가 들어간다.

 

예를 들어, study 폴더 안에 파일 a, b와 폴더 subject가 있고, 폴더 subject 안에는 파일 c가 있다고 해보자.


이때 study 폴더의 스냅샷에 해당하는 Tree 객체는 다음과 같다.

 

study 폴더의 tree 객체는 a, b 파일의 이름과 Blob 객체에 대한 참조와, 서브 디렉토리인 subject 폴더의 tree 객체의 참조를 갖는다.

그리고 subject 디렉토리의 tree 객체는 파일 c에 대한 blob 객체의 참조와 파일 c의 이름을 내용으로 갖는다.

3. Commit

Commit 객체는 디렉토리의 스냅샷에 해당하는 tree 객체에 대한 참조와 저자, 이메일 등 메타 데이터를 담고 있다.

Commit 객체 또한 내용을 바탕으로 SHA-1 해시 값이 부여된다.

 

명령어 정리

git cat-file

  • Git 내부 객체의 정보를 확인할 수 있는 명령어
  1. -t: 주어진 객체의 타입(blob, tree, commit, tag) 출력
  2. -p: 객체의 내용을 출력

git hash-object

  • 파일이나 입력사항을 blob 객체로 생성할 수 있는 명령어
  1. -w: 생성된 blob 객체를 실제로 Git 데이터베이스에 기록
  2. --stdin: 파일 대신 표준 입력으로 데이터를 받음
  • 아무런 옵션 없이 사용하면 해당 파일의 SHA-1 해시 값을 출력한다.

  • -w 옵션을 사용하면 생성된 blob 객체를 실제로 git 저장소에 저장한다. (여기서 LICENSE 파일의 내용이 license이다.)

  • -w 옵션과 --stdin 옵션을 동시에 사용하면 표준 입력으로 받은 문자열을 내용으로 하는 blob 객체를 생성한다.

git write-tree

  • Staging area 있는 파일과 디렉토리 구조 기반으로 tree 객체를 생성하는 명령어

 

 

 

따라서, 파일의 내용 하나만 바뀌더라도, tree 객체의 해시 값이 바뀌게 된다. 왜냐하면 파일의 내용이 바뀌게 된다면 그 파일의 해시 값이 바뀌게 되고, 그렇다면 그 파일의 해시값을 내용으로 담고 있는 tree객체의 내용이 바뀌는 것이 된다. 따라서 tree 객체의 해시 값이 바뀌게 된다. 이것이 git이 버전을 관리하는 원리이다.

 

시행착오

 

1. git cat-file 명령어 오류

 

- git add를 통해 파일에 대한 blob을 생성했다.

- .git/objects 디렉토리로 이동해보니, 두 글자로 된 디렉토리가 새로 생겨있었다.

- 그 디렉토리로 이동하니, blob 파일로 추정되는 파일이 하나 있었다.

- 그 파일의 이름이 해시 값인 줄 알고, 그 파일의 이름으로 git cat-file 명령어를 수행했다.

- 하지만, 그 파일에 대한 해시 값은 디렉토리 이름 2자 + blob 파일 이름 38자를 한 40자였다.

 

2. 3개의 파일을 add 했는데, 한 개의 블롭 파일만 생성됨

 

문제점

 

- 실제로 ~/git-study라는 디렉토리를 만들고, 거기에 touch README test.rb LICENSE 를 통해 세 개의 파일을 생성했다.

- 이후 git init을 통해 git-study 디렉토리를 git 저장소로 초기화하였고, git add README test.rb LICENSE를 통해 Staging Area에 올렸다.

- 나의 기대사항으론, 세 파일에 대한 blob 객체가 .git/objects 디렉토리에 세 개가 생겨야 했다.

- 하지만 실제 수행 결과는 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391' 이라는 해시를 갖는 한 개의 blob파일뿐이었다.

해결

 

1. 세 파일이 모두 비어있었기 때문에 발생한 결과였다.

  •  touch 명령어를 통해 파일의 이름만 생성했으므로 세 파일 모두 내용이 비어있었다.

2. Blob 객채는 파일의 내용에 대한 객체이다.

  • 세 파일 모두 비어있음으로 내용이 동일하므로, 각 파일에 대한 Blob이 동일하다.
  • 비어있는 내용에 대한 해시는 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391로 동일하다.

3. Git은 blob의 중복을 방지한다.

  • 동일한 내용을 가진 파일은 하나의 같은 Blob 객체를 참조한다.

따라서, 내용이 같은 개의 파일에 대한 Blob 객체는 하나만 생긴 것이다.

 

3. Blob에 대해 헷갈렸던 부분

 

나는 어떤 파일에 대한 Blob파일이 항상 40Byte의 크기로 압축된다고 이해했다. 그래서 말이안 됐다. ‘어떻게 1GB의 파일과 1MB의 파일이 동일하게 40Byte로 압축된다는 거지?”

 

실제 Blob파일의 ‘크기’는 원본 파일의 크기에 따라 달라진다. 하지만, 그 Blob 파일을 식별하는 식별자는 파일의 내용을 기반으로 SHA-1 해시를 통해 생성된 40Byte의 문자열인 것이다.

 

따라서 파일의 내용이 조금만 달라져도 그 Blob의 key값이 달라지는 것이다. 반대로, 동일한 내용의 파일은 동일한 하나의 Blob객체가 된다.

 

실습

  1. LICENSE 파일은 ‘license’라는 내용을 갖는 7Byte 크기의 파일이다.
  2. git add LICENSE를 통해 생성된 Blob은, .git/objects/04/84eba0d41636ba71fa612c78559cd6c3006cde 에 저장되었다.
  3. Blob 파일의 크기는 22Byte이다.

 

  1. README 파일은 ‘readme ‘라는 문자열을 130개 갖는 910Byte 크기의 파일이다.
  2. git add README를 통해 생성된 Blob은, .git/objects/75/d4ab6fef52abf2e91c4f1a86ea3b18838b6f3d 에 저장되었다.
  3. Blob 파일의 크기는 34Byte이다.

 

  1. test.rb. 파일은 ‘test.rb ‘라는 문자열을 910개 갖는 6,729Byte 크기의 파일이다.
  2. git add test.rb를 통해 생성된 Blob은, .git/objects/fa/854c1d873fb1c189aba81275e292719b0e2fd7 에 저장되었다.
  3. Blob 파일의 크기는 84Byte이다.

결론

  • Blob 파일의 크기는 원본 파일의 크기에 비례하는 경향이 있다.
  • 원본 파일의 크기가 많이 작을 경우, Blob파일은 원본 파일보다 크기가 수도 있다.