Skip to content

Git Data Model: Blob, Tree, Commit 🧬

Mục tiêu: Hiểu ba object types cốt lõi tạo nên toàn bộ Git.

Git không lưu trữ diffs - Git lưu trữ snapshots. Mọi thứ trong Git đều là một trong bốn loại objects: Blob, Tree, Commit, và Tag. Module này tập trung vào ba loại chính.


🎯 The Core Concept: Content-Addressable Storage

Trước khi đi vào chi tiết, hãy hiểu nguyên tắc nền tảng:

Content → SHA-1 Hash → Object Name

Mọi object trong Git được đặt tên bằng SHA-1 hash của nội dung của nó.

bash
# Tính SHA-1 của một string
echo "Hello Git" | git hash-object --stdin
# Output: 9f4d96d5b00d98959ea9960f069585ce42b1349a

# Content giống nhau = SHA-1 giống nhau = Chỉ lưu 1 lần

TẠI SAO CONTENT-ADDRESSABLE?

  1. Deduplication tự động: Cùng file = cùng SHA-1 = lưu 1 lần
  2. Integrity verification: Thay đổi 1 byte → SHA-1 thay đổi hoàn toàn
  3. Immutability: Objects không bao giờ bị sửa, chỉ tạo mới

🔵 Blob - File Content

Blob (Binary Large Object) lưu trữ nội dung file - không có filename, không có metadata, chỉ có raw bytes.

Cấu trúc

blob <size>\0<content>

Ví dụ thực tế

bash
# Tạo file
echo "Hello, Git Internals!" > hello.txt

# Xem blob sẽ có SHA-1 gì
git hash-object hello.txt
# Output: 8ab686eafeb1f44702738c8b0f24f2567c36da6d

# Stage file (tạo blob trong .git/objects/)
git add hello.txt

# Xem nội dung blob
git cat-file -p 8ab686e
# Output: Hello, Git Internals!

# Xem loại object
git cat-file -t 8ab686e
# Output: blob

Đặc điểm quan trọng

PropertyGiải thích
Không chứa filenameFilename được lưu trong Tree, không phải Blob
Không chứa permissionsPermissions cũng trong Tree
Content-onlyHai files khác tên, cùng nội dung = cùng Blob
CompressedLưu trữ dạng zlib-compressed

BLOB KHÔNG BIẾT TÊN FILE

Đây là điều nhiều người hiểu sai. Blob chỉ là content. Nếu bạn có 10 files với cùng nội dung, Git chỉ lưu 1 blob duy nhất.


🟢 Tree - Directory Structure

Tree object lưu trữ directory structure - mapping giữa filenames và blobs/subtrees.

Cấu trúc

tree <size>\0
<mode> <filename>\0<sha1-binary>
<mode> <filename>\0<sha1-binary>
...

Ví dụ thực tế

bash
# Cấu trúc thư mục
# src/
# ├── main.py
# └── utils/
#     └── helper.py

# Xem tree của commit hiện tại
git cat-file -p HEAD^{tree}

# Output:
# 100644 blob a1b2c3... main.py
# 040000 tree d4e5f6... utils

Mode Values

ModeLoạiGiải thích
100644Regular fileFile bình thường
100755ExecutableFile có quyền thực thi
120000SymlinkSymbolic link
040000DirectorySubdirectory (trỏ đến Tree khác)
160000SubmoduleGit submodule

Visual Example


🔴 Commit - The Snapshot Wrapper

Commit object là wrapper chứa metadata và trỏ đến một Tree (snapshot của entire project).

Cấu trúc

commit <size>\0
tree <tree-sha1>
parent <parent-commit-sha1>     # Optional, có thể nhiều parents
author <name> <email> <timestamp>
committer <name> <email> <timestamp>

<commit message>

Ví dụ thực tế

bash
# Xem commit object
git cat-file -p HEAD

# Output:
# tree a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
# parent u1v2w3x4y5z6a7b8c9d0e1f2g3h4i5j6k7l8m9n0
# author HPN <hpn@example.com> 1703548800 +0700
# committer HPN <hpn@example.com> 1703548800 +0700
#
# feat: add user authentication

Commit Fields Explained

FieldGiải thích
treeSHA-1 của root tree (snapshot)
parentSHA-1 của commit cha (có thể 0, 1, hoặc nhiều)
authorNgười viết code gốc
committerNgười tạo commit (có thể khác author khi cherry-pick/rebase)
messageCommit message

PARENT COMMITS

  • 0 parents: Initial commit (root commit)
  • 1 parent: Regular commit
  • 2+ parents: Merge commit

🔗 Connecting the Pieces

Bây giờ hãy xem cách ba object types liên kết với nhau:

Quan sát quan trọng:

  • config.py không đổi qua 3 commits → Cùng 1 blob B3 được reuse
  • Mỗi commit trỏ đến một tree khác nhau (snapshot khác nhau)
  • Commits link ngược về parent (history chain)

🔬 Deep Dive: Complete Object Hierarchy


🛠️ Hands-on Lab

Exercise: Tạo Objects thủ công

bash
# 1. Tạo blob thủ công
echo "Hello from raw Git!" | git hash-object -w --stdin
# Output: <sha1> - blob đã được lưu vào .git/objects/

# 2. Tạo tree từ index
git update-index --add --cacheinfo 100644 <blob-sha1> test.txt
git write-tree
# Output: <tree-sha1>

# 3. Tạo commit từ tree
echo "Manual commit" | git commit-tree <tree-sha1>
# Output: <commit-sha1>

# 4. Cập nhật branch để trỏ đến commit mới
git update-ref refs/heads/manual-branch <commit-sha1>

Exercise: Trace Object Graph

bash
# Từ HEAD, trace toàn bộ object graph
COMMIT=$(git rev-parse HEAD)
echo "Commit: $COMMIT"
git cat-file -p $COMMIT

TREE=$(git rev-parse HEAD^{tree})
echo "Tree: $TREE"
git cat-file -p $TREE

# List all blobs
git cat-file -p $TREE | while read mode type sha name; do
    echo "$type $sha $name"
done

📊 Object Type Summary

ObjectChứa gìPoints toSHA-1 dựa trên
BlobFile contentNothingContent bytes
TreeDirectory listingBlobs, other TreesMode + name + SHA-1 entries
CommitMetadata + pointerOne Tree, parent CommitsAll metadata + tree SHA

💡 Key Takeaways

HPN'S INSIGHT

"Git có đúng 3 loại data objects. Nắm vững chúng, bạn nắm vững Git."

  1. Blob = Content: Chỉ lưu nội dung, không biết tên file
  2. Tree = Structure: Map filenames → blobs/subtrees
  3. Commit = Snapshot: Metadata + pointer đến root tree
  4. Immutable: Objects không bao giờ thay đổi sau khi tạo
  5. Deduplication: Cùng content = cùng SHA-1 = lưu 1 lần

🧠 Quiz

Câu 1: Ba loại data objects cơ bản trong Git là gì?

  • [ ] A) File, Folder, Commit
  • [x] B) Blob (content), Tree (structure), Commit (snapshot + metadata)
  • [ ] C) Branch, Tag, Remote
  • [ ] D) Add, Stage, Push

💡 Giải thích: Git có 3 object types: Blob lưu nội dung file (không biết tên file), Tree map filenames → blobs/subtrees (cấu trúc thư mục), Commit chứa metadata + pointer đến root tree (snapshot).

Câu 2: Tại sao Git dùng SHA-1 hash cho mỗi object?

  • [ ] A) Để encrypt nội dung file
  • [ ] B) Để nén file nhỏ hơn
  • [x] C) Để định danh duy nhất (content-addressable), phát hiện corruption, và tự động deduplicate
  • [ ] D) Để tăng tốc độ clone

💡 Giải thích: SHA-1 hash được tính từ content → cùng content luôn cho cùng hash. Điều này cho phép: (1) phát hiện data corruption, (2) deduplication tự động (cùng content = lưu 1 lần), (3) mọi object có ID duy nhất.

Câu 3: Blob object trong Git khác với file thông thường như thế nào?

  • [ ] A) Blob chứa cả filename và content
  • [x] B) Blob chỉ chứa content, không biết tên file - tên file được lưu trong Tree object
  • [ ] C) Blob chứa content đã được encrypt
  • [ ] D) Blob là compressed version của file

💡 Giải thích: Blob chỉ lưu raw content. Tên file, permissions được lưu trong Tree object (parent). Nhờ vậy, nếu 2 files có cùng nội dung nhưng khác tên, Git chỉ lưu 1 blob (deduplication).