Git - Mô hình dữ liệu kho chứa - Học Git - Git căn bản, Git là gì?
Ở 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. 1. ...
Ở 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.
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
Nếu bạn cố tình mở file đó ra bằng notepad thì sẽ có giá trị dạng như thế này:
Vì vậy ta đừng quan tâm đến nội dung bên trong các file đó nhé.
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 (xem bài ba trạng thái), 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
.
Tạo một nhánh testing
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.
Tham khảo: https://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is