Giải phẫu Git

Huy NguyenHuy Nguyen
7 min read

Nếu bạn là developer chắc chắn bạn đã dùng git, không những thế bạn còn dùng chúng hàng ngày luôn. vậy có bao giờ bạn tự hỏi git nó được cấu tạo như nào hay không. Chuyện gì nếu ta commit code, hay merge code xử lí như thế nào?

Trong bài hôm nay chúng ta hãy cùng "giải phẫu" git nhé, một phần mềm mang tính cách mạng trong giới lập trình. Hãy chắc chắn bạn đã nằm lòng cách sử dụng một số lệnh cơ bản như git commit, git branch, git merge nhé.

Tất cả mọi thứ là hash

Đúng vậy bạn không nghe nhầm đâu, thế giới bên trong git chỉ toàn là hash và hash, chính code của bạn cũng là hash đó. Bạn hãy nắm rõ ý tưởng này nhé vì nó liên quan đến phần tiếp.

Git Object - blob, tree và commit

Trước tiên sơ lược qua về git, thì thành phần cơ bản nhất của git là object. Object ở đây có thể là blob hoặc tree hoặc commit. Và tất nhiên, tất cả chúng đều đã được định danh bằng 1 mã hash.

Blob

  • binary large object: chứa nội dung code của chúng ta. Blob khác file ở chỗ file thì có chứa thêm meta data (như ngày tạo hay tên file), còn blob thì không, nó chỉ chứa nội dung file là raw byte

  • Blob được định danh bằng 1 mã hash SHA-1 bằng cách hash từ content của blob

    Có thể là đồ họa về văn bản

Tree

  • Ánh xạ từ cấu trúc thư mục code cùa chúng ta

  • Cũng được định danh bằng mã hash SHA-1

  • Content của nó trỏ đến mã hash của blob hay tree khác. Tưởng tượng rằng blob là nút lá, còn tree là một nút khác nút lá ở trên cây

  • ở ví dụ trên, tree tương đương với 1 file system với root dir có 1 file /test.js và 1 thư mục con /docs. Thư mục /docs có 2 file /docs/pic.png và /docs/1.txt.

    Có thể là đồ họa về bản thiết kế và văn bản cho biết '841B9 TREE: CAFE7 DOCS BLOB: F0OD1 TEST.JS TREE CAFE70 BLOB: F92A0 PIC.PNG BLOB: 73D8A 1.TXT F92A00) BLOB F0OD1 D8 A1 31 8A1310F.. OF.. 73D8Au @JEST HELLO WORLD'

Commit

  • Commit thì quá quen rồi, nó là kết quả của câu lệnh git commit ta dùng hàng ngày. Commit giống như snapshot, ghi lại thời trạng thái của toàn bộ thư mục tại thời điểm đó.

  • Commit gồm có 1 pointer trỏ đến hash của root tree, author - người commit, message - nội dung commit message, và thời gian commit. Đặc biệt nhất commit còn trỏ đến 1 hoặc nhiều commit gần nhất của nó (trường hợp nhiều chính là khi ta merge code) gọi là commit cha

  • Tất nhiên nó vẫn được định danh bằng hash SHA-1

  • Mỗi commit là toàn bộ snapshot khi ta git commit, chứ không đơn giản là chỉ chứa những sự thay đổi so với lần commit trước đó

    Có thể là hình ảnh về sơ đồ tầng, bản thiết kế và văn bản

Góc giải đáp

Đến đây, sẽ có bạn thắc mắc, nếu commit mỗi lần đều chứa toàn bộ snapshot tại thời điểm đó, vậy ta sẽ phải lưu rất rất nhiều data mỗi lần commit chứ nhỉ.

OK hãy cùng đi luôn vào 1 ví dụ nhé.

  • Ở trên cây thư mục trên, giả sử ta đổi nội dung file 1.txt có content là HELLO WORLD thành HELLO WORLD! Ở đây có thể thấy tree đã thay đổi và blob đã thay đổi (màu đỏ)

    Có thể là đồ họa về văn bản

  • Thoạt nhìn có thể thấy là commit mới lưu rất nhiều data, nhưng nếu nhìn kĩ thì ta thấy, các phần không thay đổi vẫn được giữ nguyên.

  • Kết luận, nếu object không đổi, ta không cần tạo thêm clone của object đó mà vẫn giữ nguyên

    Có thể là đồ họa về bản thiết kế, bản đồ, sơ đồ tầng và văn bản

Recap

  • Blob: nội dung của file

  • Tree: ánh xạ thư mục, trỏ đến các blob và tree khác

  • Commit: snapshot của working tree

  • Giả sử tôi có 1 file có content "Hello world" và bạn cũng có 1 file tương tự như vậy. Vậy mã hash blob của nó là như nhau vì đơn giản nó hash từ nội dung của file

  • Giả sử tôi có 1 folder chứa folder con và các files khác. Bạn cũng có 1 folder y nguyên như vậy từ tên file cho đến cấu trúc,.. Vậy mã hash tree là giống nhau

  • Nhưng nếu bạn commit, tôi cũng commit thì khả năng cao mã hash là khác nhau vì nó hash từ các nôi dung Tác giả, commit message, thời gian commit

Branch

  • Branch chỉ đơn giản là 1 cách định danh do người dùng tạo ra để trỏ đến commit.

  • Branch sẽ trỏ tới commit mới nhất.

  • Khi ta dùng git branch, cả 2 branch cùng trỏ tới 1 commit.

  • Con trỏ HEAD cho biết ta đang làm việc ở branch nào. Thông thường nó sẽ trỏ tới branch, khi ta checkout về 1 commit trong quá khứ thì HEAD sẽ trỏ trực tiếp tới commit

Cách hoạt động

  • Branch hiện tại là master, đang trỏ tới commit mới nhất.

    Không có mô tả ảnh.

  • Khi ta "git branch test", git tạo thêm con trỏ branch test. 2 con trỏ cùng trỏ vào commit mới nhất.

    Không có mô tả ảnh.

  • Khi ta làm việc trên master, HEAD trỏ trực tiếp tới master

    Không có mô tả ảnh.

  • khi "git checkout test", HEAD trỏ về test

    Không có mô tả ảnh.

  • khi ta git commit trên nhánh test, test trỏ tới commit mới nhất, HEAD vẫn trỏ về test

    Không có mô tả ảnh.

  • git check out master, HEAD lại trỏ về master

    Không có mô tả ảnh.

  • git commit trên master, master trỏ tới commit mới nhất.

    Không có mô tả ảnh.

Cách git lưu lại sự thay đổi

  • Khi ta làm việc với source code tức là ta đang làm việc với working dir (chính là folder chứa folder .git)

  • Sau khi chúng ta thay đổi code, ta muốn lưu lại code đó, thì lúc này nó sẽ được lưu trữ trên Repository.

    Không có mô tả ảnh.

  • Tuy nhiên code không bay thằng từ Working dir đến Repo, mà nó phải qua một vùng trung gian là Index hay Staging Area

    Không có mô tả ảnh.

  • File trong working dir có 2 trạng thái: Tracked và Untracked. Tracked là các file đã được commit hoặc nằm ở staging area. Untracked là các file còn lại.

    Không có mô tả ảnh.

Test lý thuyết

Lý thuyết đủ rồi, giờ hãy kiểm chứng lại bằng thực nghiệm nhé.

  • Đầu tiên mình sẽ init 1 repo có tên là repo_1 bằng lệnh git init repo_1

  • Để kiểm tra kết của câu lệnh trên, ta có thể sử dụng lệnh tree /f .git. Kết quả là cấu trúc các file có trong foler .git.

    Không có mô tả ảnh.

  • Hãy tạo 1 file bất kì trong folder, ví dụ lệnh: echo hello > new_file.txt

    Không có mô tả ảnh.

  • Tiếp đến ta add rồi commit file vừa rồi. Sau đó chạy lại lệnh tree /f .git, thu được kết quả

    Không có mô tả ảnh.

  • Đã có rất nhiều thay đổi. Ở đây bạn chú ý rằng trong foler objects các hash có cùng 2 kí tự đầu sẽ chung 1 folder và có tên là 2 kí tự đó.

  • Ta để ý folder logs là nơi ghi lại lịch sử các thao tác với repo như là commit, merge, rebase, reset,...; folder refs chứa các con trỏ branch và tag và folder objects chứa.

Cảm ơn các bạn đã theo dõi!

0
Subscribe to my newsletter

Read articles from Huy Nguyen directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Huy Nguyen
Huy Nguyen

I am a software engineer with 4 years of experience in developing web applications. My expertise lies in backend development, and I have a deep interest in problem-solving, algorithms, system design, and databases. I am always eager to learn and embrace challenging projects, striving to deliver applications that exceed user expectations. I also love sharing my knowledge and learning from others to foster mutual growth and improvement