Understanding Git - Data Model

|-- objects
|   |-- 5d
|   |   |-- 92c127156d3d86b70ae41c73973434bf4bf341
|   |-- a6
|   |   |-- bdf05551541dc86b7a49212b62cfe1e9bb14fe
|   |-- cf
|   |   |-- 59e02c3d2a2413e2da9e535d3c116af1077906
|   |-- f8
|   |   |-- 9e64bdfcc08a8b371ee76a74775cfe096655ce
|   |-- info
|   |-- pack

              +-----------------+
              |     a6dbf0      |
              +-----------------+
              | tree     f89e64 |
              +-----------------+
                       |
                       v
              +-----------------+
              |     f89e64      |
              +-----------------+
              |cf59e0  README.md|
              |5d92c1  index.php|
              +-----------------+
                     |    |
         +-----------+    +-----------+
         |                            |
         v                            v
+-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |
+-----------------+          +-----------------+
| <?php echo      |          | # Description   |
| "Hello World";  |          |This is my hello |
|                 |          |world project    |
+-----------------+          +-----------------+

2005년에 시작된 이래로 git 은 오픈 소스 세계에서 대중적으로 인기를 얻고있다. 매우 유명한 VCS 도구들이 있지만 이와 성격은 조금 다르다. 최근에는 포스트에도 사용하고 있다. Edward Thomson이 강연 한 Deep Dive Into Git 을 확인하는것이 크게 도움된다:

“The Git commands are just a leaky abstraction over the data storage.” Git commands는 data storage에 대한 leaky abstraction 이다.

git의 data model을 알아보자.

1. git init

시작하려면 프로젝트 디렉토리에서 git 저장소를 초기화한다:

$ git init

Git은 프로젝트 디렉토리에 .git 디렉토리를 생성한다:

$ tree .git/

.git/  
|-- HEAD  
|-- config  
|-- description  
|-- hooks  
|   |-- applypatch-msg.sample  
|   |-- commit-msg.sample  
|   |-- post-update.sample  
|   |-- pre-applypatch.sample  
|   |-- pre-commit.sample  
|   |-- pre-push.sample  
|   |-- pre-rebase.sample  
|   |-- pre-receive.sample  
|   |-- prepare-commit-msg.sample  
|   |-- update.sample  
|-- info  
|   |-- exclude  
|-- objects  
|   |-- info  
|   |-- pack  
|-- refs  
    |-- heads  
    |-- tags

8 directories, 14 files

이 파일들과 디렉토리 중 일부는 익숙한 모습이지만 (특히, HEAD), 우선 비어있는 .git/objects 디렉토리에 집중하여 보자.

  • index.php 파일 생성
$ touch index.php
  • 내용 추가
<?php  
echo "Hello World";
  • README.md 파일 생성
$ touch README.md
  • 내용 추가
# Description  
This is my hello world project
  • commit
$ git add .  
$ git commit -m "Initial Commit"

.git 디렉토리에서 다시 한번 살펴보면 .git/object 디렉토리에 몇개의 서브 디렉토리와 파일이 있음을 알 수 있다.

|-- objects  
|   |-- 5d  
│   │   |-- 92c127156d3d86b70ae41c73973434bf4bf341  
|   |-- a6  
│   │   |-- dbf05551541dc86b7a49212b62cfe1e9bb14f2  
|   |-- cf  
│   │   |-- 59e02c3d2a2413e2da9e535d3c116af1077906  
|   |-- f8  
│   │   |-- 9e64bdfcc08a8b371ee76a74775cfe096655ce  
|   |-- info  
|   |-- pack

.git/objects에 구성된 디렉토리 이름이 2문자 길이라는 것에 주목하여야 한다. Git은 모든 객체에 대해 40자 체크섬 (SHA-1) 해시를 생성하고 그 체크섬의 처음 두 문자를 디렉토리 이름으로 사용하고 다른 하나는 파일(object) 이름으로 사용합니다.

어떤 파일을 커밋 할 때 git이 생성하는 첫 번째 종류의 객체는 blob 객체이다. (생성 및 수정한 파일에 대한 객체)

+-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |
+-----------------+          +-----------------+
| <?php echo      |          | # Description   |
| "Hello World";  |          |This is my hello |
|                 |          |world project    |
+-----------------+          +-----------------+
index.php 및 README.md 파일과 관련된 Blob 객체

여기는 파일의 스냅샷(커밋 당시의 파일 내용)과 체크섬 헤더가 있다.

2. tree objects

프로젝트에 있는 모든 파일의 목록을 포함하는 blob 객체에 대한 포인터가 들어가 있다. (git 이 파일을 blob 객체와 연관시키는 방법)

              +-----------------+
              |     f89e64      |
              +-----------------+
              |cf59e0  README.md|
              |5d92c1  index.php|
              +-----------------+
                     |    |
         +-----------+    +-----------+
         |                            |
         v                            v
+-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |
+-----------------+          +-----------------+
| <?php echo      |          | # Description   |
| "Hello World";  |          |This is my hello |
|                 |          |world project    |
+-----------------+          +-----------------+
BLOB 객체를 가리키는 Tree 객체

자식 객체는 다른 정보와 함께 트리 객체에 대한 포인터를 가진 커밋 객체를 만든다:

              +-----------------+
              |     a6dbf0      |
              +-----------------+
              | tree     f89e64 |
              +-----------------+
                       |
                       v
              +-----------------+
              |     f89e64      |
              +-----------------+
              |cf59e0  README.md|
              |5d92c1  index.php|
              +-----------------+
                     |    |
         +-----------+    +-----------+
         |                            |
         v                            v
+-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |
+-----------------+          +-----------------+
| <?php echo      |          | # Description   |
| "Hello World";  |          |This is my hello |
|                 |          |world project    |
+-----------------+          +-----------------+
객체 포인터가 트리 객체에 커밋 됨
  • .git/object 디렉토리는 아래와 같이 보일 것이다.
|-- objects  
|   |-- 5d  
│   │   |-- 92c127156d3d86b70ae41c73973434bf4bf341  
|   |-- a6  
│   │   |-- dbf05551541dc86b7a49212b62cfe1e9bb14f2  
|   |-- cf  
│   │   |-- 59e02c3d2a2413e2da9e535d3c116af1077906  
|   |-- f8  
│   │   |-- 9e64bdfcc08a8b371ee76a74775cfe096655ce  
|   |-- info  
|   |-- pack
  • git log를 사용하여 커밋 기록을 확인할 수 있다:
commit a6dbf05551541dc86b7a49212b62cfe1e9bb14f2   
Author: atom <[atom@gmail.com](mailto:atom@gmail.com)>  
Date:   Tue Jan 23 13:31:43 2018 +0100

Initial Commit
  • naming 규칙을 사용하면 .git/object에서 commit 객체를 찾을 수 있다:
|-- objects  
|   |-- a6  
│   │   |-- dbf05551541dc86b7a49212b62cfe1e9bb14f2
  • cat-file을 활용하여 파일을 보면:
$ git cat-file commit a6dbf05551541dc86b7a49212b62cfe1e9bb14f2
  • 커밋 객체의 내용을 가져오려면:
tree f89e64bdfcc08a8b371ee76a74775cfe096655ce  
author atom <[atom@gmail.com](mailto:atom@gmail.com)> 1516710703 +0100  
committer atom <[atom@gmail.com](mailto:atom@gmail.com)> 1516710703 +0100

Initial Commit
  • 여기에 커밋의 트리 객체에 대한 포인터가 있으며 git ls-tree 명령을 사용하여 내용을 검사:
$ git ls-tree f89e64bdfcc08a8b371ee76a74775cfe096655ce
  • 그리고 예상대로 그것은 blob 객체에 대한 포인터가있는 파일 목록을 포함:
100644 blob cf59e02c3d2a2413e2da9e535d3c116af1077906 README.md  
100644 blob 5d92c127156d3d86b70ae41c73973434bf4bf341 index.php
  • cat-file 명령을 사용하여 (예를 들어) index.php 를 나타내는 blob 객체를 볼 수 있음:
$ git cat-file blob 5d92c127156d3d86b70ae41c73973434bf4bf341
  • index.php 파일의 내용이 포함 된 것을 볼 수 있다.
<?  
echo "Hello World!"

3. commit

              +-----------------+
              |     a6dbf0      |
              +-----------------+
              | tree     f89e64 |
              +-----------------+
                       |
                       v
              +-----------------+
              |     f89e64      |
              +-----------------+
              |cf59e0  README.md|
              |5d92c1  index.php|
              +-----------------+
                     |    |
         +-----------+    +-----------+
         |                            |
         v                            v
+-----------------+          +-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |          |     6d725d      |
+-----------------+          +-----------------+          +-----------------+
| <?php echo      |          | # Description   |          | <?php echo      |
| "Hello World";  |          |This is my hello |          | "Hello World";  |
|                 |          |world project    |          |//some code magic|
+-----------------+          +-----------------+          +-----------------+
git은 변경된 파일에 대한 새 BLOB 개체를 생성

index.php 의 새로운 스냅샷으로 신규 blob 객체를 만들었다. README.md 는 변경되지 않았기 때문에, 새로운 blob 객체가 생성되지 않는다. git은 대신 기존의 객체를 재사용한다.

git이 트리 객체를 만들면 index.php 에 할당 된 blob 포인터가 업데이트되고 README.md 에 할당 된 blob 포인터는 이전 커밋의 트리와 동일하게 유지된다.

              +-----------------+
              |     a6dbf0      |
              +-----------------+
              | tree     f89e64 |
              +-----------------+
                       |
                       v
              +-----------------+               +-----------------+
              |     f89e64      |               |     6f2109      |
              +-----------------+               +-----------------+
              |cf59e0  README.md|               |cf59e0  README.md|
              |5d92c1  index.php|               |6d725d  index.php|
              +-----------------+               +-----------------+
                     |    |                            |    |
         +-----------+    +-----------+    +-----------+    +-----------+
         |                            |    |                            |
         v                            v    v                            v
+-----------------+          +-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |          |     6d725d      |
+-----------------+          +-----------------+          +-----------------+
| <?php echo      |          | # Description   |          | <?php echo      |
| "Hello World";  |          |This is my hello |          | "Hello World";  |
|                 |          |world project    |          |//some code magic|
+-----------------+          +-----------------+          +-----------------+
index.php BLOB에 대한 포인터가 업데이트되고 README.md BLOB에 대한 포인터는 그대로 유지

그리고 결국 git은 트리 객체에 대한 포인터를 가진 commit 객체를 생성한다.

              +-----------------+               +-----------------+
              |     a6dbf0      |               |     5ebdb4      |
              +-----------------+ <------------ +-----------------+
              | tree     f89e64 |               | tree     6f2109 |
              +-----------------+               | parent   a6dbf0 |
                       |                        +-----------------+
                       |                                 |
                       v                                 v
              +-----------------+               +-----------------+
              |     f89e64      |               |     6f2109      |
              +-----------------+               +-----------------+
              |cf59e0  README.md|               |cf59e0  README.md|
              |5d92c1  index.php|               |6d725d  index.php|
              +-----------------+               +-----------------+
                     |    |                            |    |
         +-----------+    +-----------+    +-----------+    +-----------+
         |                            |    |                            |
         v                            v    v                            v
+-----------------+          +-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |          |     6d725d      |
+-----------------+          +-----------------+          +-----------------+
| <?php echo      |          | # Description   |          | <?php echo      |
| "Hello World";  |          |This is my hello |          | "Hello World";  |
|                 |          |world project    |          |//some code magic|
+-----------------+          +-----------------+          +-----------------+
Commit 객체는 트리를 가리키며 부모 커밋 객체에 대한 포인터를 가지고 있음

또한 부모 커밋 객체에 대한 포인터이다. (첫번째 커밋을 제외한 모든 커밋은 적어도 하나의 부모를 가진다).

4. file delete

              +-----------------+               +-----------------+
              |     5ebdb4      |               |     379cb3      |
              +-----------------+ <------------ +-----------------+
              | tree     6f2109 |               | tree     69d524 |
              | parent   a6dbf0 |               | parent   5ebdb4 |
              +-----------------+               +-----------------+
                       |                                 |
                       v                                 v
              +-----------------+               +-----------------+
              |     6f2109      |               |     69d524      |
              +-----------------+               +-----------------+
              |cf59e0  README.md|               |cf59e0  README.md|
              |6d725d  index.php|               |                 |
              +-----------------+               +-----------------+
                     |    |                            |
         +-----------+    +-----------+    +-----------+
         |                            |    |
         v                            v    v                            
+-----------------+          +-----------------+
|     5d92c1      |          |     cf59e0      |
+-----------------+          +-----------------+
| <?php echo      |          | # Description   |
| "Hello World";  |          |This is my hello |
|//some code magic|          |world project    |
+-----------------+          +-----------------+
자식은 트리 객체에서 index.php에 대한 엔트리를 삭제

git은 tree 객체에서 파일 항목 (BLOB 객체에 대한 포인터로 파일 이름)을 삭제한다. 이 경우 커밋에서 index.php 를 삭제 했으므로 더 이상 커밋의 트리 객체에 index.php 엔트리가 없다. (즉, 커밋의 트리 객체에는 더 이상 index.php 를 나타내는 blob 객체에 대한 포인터가 없다.)

데이터 모델 에는 트리 객체를 중첩 할 수 있는 추가 기능이 한 가지 더 있다. (다른 트리 개체를 가리킬 수 있음). 모든 BLOB 객체는 파일을 나타내고 모든 트리 객체는 디렉토리를 나타내므로 중첩 된 디렉토리가 있으면 중첩 된 트리 객체를 갖게된다.

  • 예제
           +-----------------+
           |      bf4513     |
           +-----------------+
           | tree     6f1183 |
           +-----------------+
                    |
                    v
           +-----------------+
           |      6f1183     |
           +-----------------+
           | cb31c9 README.md|
           | eabb42      app |
           +-----------------+
                   |  |
         +---------+  +-----------+
         |                        |
         v                        v
+----------------+     +-------------------+
|     cb31c9     |     |     eabb42        |
+----------------+     +-------------------+
| # Description  |     | cb31c9     app.php|
|example project |     | 0bdda9 app_dev.php|
+----------------+     +-------------------+
                               |  |
                +--------------+  +----------+
                |                            |
                v                            v
        +----------------+    +----------------------+
        |     cb31c9     |    |     0bdda9           |
        +----------------+    +----------------------+
        | <?php          |    | <?php                |
        |// our app      |    |// our app in dev env |
        +----------------+    +----------------------+
트리 객체는 다른 트리 객체를 가리킬 수 있다

하나의 README.md 파일과 두 개의 파일을 가진 하나의 app 디렉토리를 가진다. (app.phpapp_dev.php)

Git은 blob 객체를 사용하여 주어진 시점 (커밋) 및 프로젝트의 폴더 구조를 재현 할 수있는 트리 객체에서 파일 내용을 재생성한다.