GIT - Data Model
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.php
와 app_dev.php
)
Git은 blob 객체를 사용하여 주어진 시점 (커밋) 및 프로젝트의 폴더 구조를 재현 할 수있는 트리 객체에서 파일 내용을 재생성한다.