Git – Mô hình dữ liệu kho chứa

Ở các chương trước mình đã giới thiệu một số lệnh làm việc với branch nhưng chưa giải thích về quy trình hoạt động của nó, lý do là mình không muốn bạn tập trung vào lý thuyết ở phần đầu vì sẽ khó hiểu, nên trong chương này mình sẽ giải thích kỹ một số khái niệm, hy vọng bạn sẽ dể hiểu hơn.

Mục lục

1. Git Objects

Khi chúng ta chạy lệnh $ git init thì một thư mục ẩn được tạo ra đó là .git, thư mục này sẽ chứa toàn bộ database và các thao tác của dự án, nó có một số thư mục con và một số file quan trọng gồm: HEAD, branches/, config, description, hooks/ index, info/, objects/, refs/.

Trong đó bạn cần chú ý đến thư mục objects vì đây là thư mục chứa toàn bộ database, nó có 4 objects như sau:

  • tree: là directory
  • blob: là file
  • commit
  • tag

Nếu bạn đã từng tìm hiểu qua Linux thì sẽ thấy cách lưu trữ của Git gần giống như Linux bởi vì tiền thân Git được tạo ra là sử dụng trên Linux. Mỗi object sẽ được định danh bởi một Unique Id có chiều dài là 40 kí tự dựa vào thuật toán băm SHA-1. Ví dụ73b41f9f027614a0417edc7cca83ea5065eb36

2. Mô hình đối tượng 1 branch

Trước tiên chúng ta cần xem lại cách tổ chức lưu trữ của Git.

Khi bạn thực hiện một commit, Git sẽ tạo một đối tượng commit và có chứa các con trỏ trỏ tới ảnh của nội dung mà bạn đã tổ chức tại stage, ngoài ra còn chứa thông tin tác giả, thông điệp, cũng có thể chứa các con trỏ khác trỏ tới các commit cha của commit đó. Commit đầu tiên sẽ không có cha, commit thứ ha trở đi sẽ có một cha hoặc nhiều cha nếu nó được merge từ nhiều branch.

Để dễ hình dùng thì chúng ta cùng phân tích một ví dụ trên trang git-scm.com.

Giả sử có 3 tập tin và bạn tổ chức cả ba tập tin vào stage để commit (README test.rb LICENSE), lúc này bạn sẽ thực hiện lệnh commit như sau:

git add README test.rb LICENSE
git commit -m "Khoi dong du an"
[master 517333f] 
Khoi dong du an 3 files changed, 
0 insertions(+), 
0 deletions(-) 
create mode 100644 LICENSE 
create mode 100644 README 
create mode 100644 test.rb

Lênh commit sẽ băm cả 3 file và lưu chúng dưới dạng đối tượng tree (thư mục), trong tree sẽ chứa tất cả các blob (tức là 3 file trên) và mỗi blod sẽ trỏ đên file gốc của nó. Sau đó nó tạo một đối tượng commit chứa các thông tin metadata như author, email, message … và đặc biệt là đối tượng commit này có một con trỏ trỏ tới đối tượng tree, vì vậy ta có thể tái tạo lại history thông qua đối tượng commit này.


Dữ liệu kho chứa của một commit (snapshot)

Như vậy đối tượng tree và các blod ta gọi là snapshot.

Nếu bạn thay đổi dữ liệu cho một trong ba file và tiếp tục thực hiện một lệnh commit thì lúc này ngoài các thông tin như trên, đối tượng commit còn tạo thêm một con trỏ và trỏ tới đối tượng commit trước đó. Sau 2 commit thì lúc này mô hình kho chứa sẽ như sau:

Các đối tượng dữ liệu của nhiều commit

3. Mô hình đối tượng nhiều branch

Khi bạn tạo một nhánh (branch) thực chất là bạn đang tạo một con trỏ và trỏ tới commit mới nhất tại thời điểm thực thi lệnh. Chúng ta có tên nhánh mặc định là Master Branch. Như ở ví dụ trên thì chúng ta đang thực thi các commit tại branch master nên mô hình lúc này dạng như sau:

Master branch trỏ tới commit mới nhất

Vấn đề bây giờ là lúc bạn tạo thêm một branch mới thì mô hình sẽ như thế nào? Như mình đã nói ở trên, khi bạn tạo một nhánh mới tức là bạn đã tạo một con trỏ và trỏ tới commit mới nhất. Như hình dưới đây là mình đã tạo một nhánh testing và trỏ tới commit có địa chi là f30ab.

4. Con trỏ HEAD

Nếu bạn tạo nhiều branch thì làm sao để biết là đang làm việc trên branch nào? Git có một con trỏ đặc biệt tên là HEAD, đây là con trỏ sẽ trỏ tới một branch nội bộ trong repository của bạn. Như trong hình dưới đây là bạn đang làm việc trên nhánh master vì con trỏ HEAD trỏ tới master.

HEAD trỏ tơi master nên chúng ta đang làm việc tại master

Giả sử bạn chuyển sang branch testing với lệnh checkout.

git checkout testing

Lúc này HEAD sẽ trỏ tới branch testing.

HEAD trỏ tơi testing nên chúng ta đang làm việc tại testing

Bây giờ ta sẽ thực hiện một commit tại branch testing, lúc này mô hình sẽ được cập nhật, branch testing sẽ có một commit đi trước branch master.

vim test.rb$ git commit -a -m 'made a change'

Lưu ý: Lệnh vim là edit file

Branch testing đi trước master 1 commit

Bây giờ bạn hãy chuyển sang branch master.

git checkout master

Mô hình sẽ như sau:

HEAD sẽ trỏ tới master

Nếu bạn thực hiện một commit tại master thì lịch sử sẽ bị tách ra.

vim test.rb$ git commit -a -m 'made other changes'

Lịch sử tách ra làm hai nhánh.

Như vậy lịch sử lúc này được chia làm hai, và bạn có thể thay đổi lịch sử dựa vào địa chỉ của các đối tượng commit.

6. Lời kết

Bài này khá hay, mình viết bài này chủ yếu dịch lại từ trang chủ của git.

Điểm nhấn mạnh của bài này là nói về mô hình hoạt động của GIT khi thực hiện các thao tác commit và tạo branch mới, bạn cần phải đọc thật kĩ để hiểu mô hình này thì các bài sau mới dễ hiểu bài.