--------------- <> -----------------
--- KHOA HỌC - CÔNG NGHỆ - GIÁO DỤC - VIỆC LÀM ---
--- Học để đi cùng bà con trên thế giới ---

Tìm kiếm trong Blog

Git thực hành (6) - Nguyên tắc làm việc của Git

Bài trước: Git thực hành (5) - Các khu vực làm việc của Git (tiếp)
-----

6. Nguyên tắc làm việc của Git

Tới bài học này, bạn đã được thực hành, trải nghiệm với Git, như:

- Nhúng Git vào thư mục dự án bằng lệnh git init, lệnh này sẽ tạo ra Thư mục làm việc (working directory, working tree), và Kho chứa (repository)

- Đánh dấu tập tin/thư mục sẽ được commit, thông tin đánh dấu này được ghi trong Khu tạm (staging area, tập tin .git\INDEX)

- Tạo ra 1 phiên bản của dự án (với các thông tin đã được đánh dấu trong .git\INDEX) và lưu phiên bản vào Kho chứa .git (lệnh git commit)

Trong phần này, chúng ta cùng tìm hiểu kỹ hơn về nguyên tắc làm việc của Git.

6.1 Chụp ảnh

Thông thường, khi bạn cần lưu trữ các phiên bản khác nhau của mỗi tập tin bạn sẽ làm như thế nào? 

Như trong phần “quản lý phiên bản cục bộ” đã đề cập: khi một tập tin có thay đổi và cần lưu thành một phiên bản khác, bạn sẽ lưu nó thành một tập tin mới với tên mới. Nghĩa là tập tin mới có thay đổi một lượng delta nào đó so với tập tin ban đầu. Để tối ưu việc lưu trữ, thực tế chỉ cần lưu tập tin gốc, cộng thêm thông tin của mỗi lần thay đổi (delta).

Khi đó, thông tin một phiên bản của dự án sẽ gồm các tập tin gốc, cùng với các thay đổi của từng tập tin. Đây chính là cách tiếp cận của đa số các hệ thống quản lý phiên bản, như: CVS, Subversion (SVN), Perforce, Bazaar. Xem hình minh họa.

Với Git thì khác, để lưu lại một phiên bản của dự án, Git sẽ chụp ảnh (snapshot) toàn bộ các tập tin của dự án. Tập tin nào có thay đổi thì “chụp” (tạo ra một ảnh mới cho tập tin); tập tin nào không thay đổi thì không “chụp”, mà chỉ tạo một liên kết, trỏ về phiên bản trước của nó. Xem hình minh họa.

Có thể thấy, dữ liệu trong Kho chứa của Git gồm một loạt các “ảnh chụp” của hệ thống tập tin đơn giản (miniature filesystem). Mỗi “ảnh chụp” là một phiên bản của dự án, tương đương với một “commit” trong hệ thống Git.

6.2 Một số đặc điểm của Git

Phần lớn các thao tác với Git được thực hiện trên máy cục bộ

Khi làm việc với Git, hầu hết các thông tin và tài nguyên đều có sẵn trên máy cục bộ, do vậy bạn không phải kết nối tới server ở xa.

Ví dụ, nếu bạn muốn xem lại thông tin hoặc nội dung các phiên bản trước của dự án, thì bạn chỉ việc xem trong cơ sở dữ liệu của Git tại máy cục bộ. Hoặc nếu bạn muốn lưu lại phiên bản hiện thời của dự án (commit), bạn cũng thực hiện trên máy cục bộ. Chỉ khi nào bạn muốn chia sẻ phiên bản mới cho thành viên khác thì mới cần tới mạng để tải phiên bản mới lên server (push).

Git đảm bảo tính toàn vẹn

Mọi thứ trong Git (ví dụ tập tin) đều được tạo mã kiểm tra (checksum) trước khi lưu vào cơ sở dữ liệu Git. Git tạo ra mã kiểm tra bằng cách chạy một thuật toán (SHA-1) dựa vào nội dung của tập tin.

Kết quả của thuật toán SHA-1 sẽ tạo ra một số hexa, tạm gọi là mã SHA-1, có chiều dài 40 ký số. Ví dụ, một mã SHA-1: 24b9da6552252987aa493b52f8696cd6d3b00373

Git dùng mã SHA-1 của tập tin để đặt tên mới cho nó và thao tác với tên mới này chứ không không quan tâm đến tên thật của tập tin.

Giả sử bằng cách nào đó, nội dung của một tập tin bị thay đổi, khi Git mở tập tin, nó sẽ tính lại mã SHA-1, so với mã SHA-1 đã được lưu trước đây, nếu hai mã này không khớp, Git sẽ phát hiện ra là nội dung của tập tin đã bị thay đổi.

Như vậy không thể có tình trạng nội dung của tập tin bị thay đổi mà Git không biết, cơ chế này đảm bảo dữ liệu do Git quản lý luôn có tính toàn vẹn.

Git chỉ “thêm” dữ liệu

Trong quá trình làm việc với Git, hầu như mọi thao tác đều được Git ghi vào cơ sở dữ liệu của nó. Bạn có thể bị mất hoặc bị xáo trộn thông tin nếu chưa lưu phiên bản (commit), nếu bạn đã lưu phiên bản thì rất khó bị mất dữ liệu, đặc biệt nếu bạn thường xuyên đẩy cơ sở dữ liệu Git lên server (push) hoặc tới kho chứa khác.

Cách làm việc theo kiểu luôn “thêm” dữ liệu (không xóa) tạo ra cảm giác yên tâm cho người sử dụng.

6.3 Ba trạng thái quan trọng của tập tin

Khi một tập tin (thư mục) đã được Git quản lý, nó có thể ở các trạng thái sau: modified, staged, committed.

- modified: tập tin đã bị thay đổi (nội dung hoặc tên), nhưng chưa được được lưu vào Kho chứa (chưa commit)

- staged: tập tin đã bị thay đổi, đã được đánh dấu vào trong Khu tạm

- committed: là tập tin đã bị thay đổi, đã được đánh dấu và đã được lưu vào Kho chứa

Ba trạng thái của tập tin tương ứng với 3 vùng làm việc của Git.

- Tập tin đã bị thay đổi, mà chưa commit sẽ nằm trong Thư mục làm việc. Như đã biết, Thư mục làm việc là bản sao một phiên bản của dự án, nó được lấy ra từ Kho chứa (.git). Đây là thư mục làm việc của lập trình viên, họ thao tác trên nó như một thư mục bình thường

- Tập tin đã bị thay đổi, được đánh dấu trong Khu tạm. Khu tạm là một tập tin (index) nằm trong thư mục .git, chứa thông tin về những gì sẽ được lưu phiên bản (commit) trong lần tới

- Tập tin tin đã bị thay đổi, đã được đánh dấu và đã được lưu vào Kho chứa. Kho chứa là thư mục .git, là nơi Git lưu trữ “siêu dữ kiện” (metadata), và cơ sở dữ liệu của Git (không phải cơ sở dữ liệu của ứng dụng đang được phát triển). Đây là phần quan trọng nhất của Git, nó là thành phần được sao lưu (copy) về khi bạn nhân bản một kho chứa từ máy khác (clone)

Xem hình minh họa.


Về cơ bản, tiến trình công việc (workflow) khi thao tác với Git gồm:

- Bạn thao tác (tạo mới, thêm dữ liệu, sửa, xóa) với tập tin/thư mục trong Thư mục làm việc (working directory)

- Đưa vào Khu tạm (staging area) các tập tin/thư mục mà bạn muốn lưu trữ vào Kho chứa (.git directory)

- Lưu phiên bản cho các nội dung đã được đánh dấu trong Khu tạm (staging area), cụ thể là thực hiện lệnh commit. Các tập tin/thư mục đã được đánh dấu trong Khu tạm sẽ được lưu trữ an toàn vào .git

Tóm lại: một tập tin nằm trong .git được xem là đã được committed. Nếu tập tin đã bị thay đổi và đã được đưa vào staging area được xem là staged. Và nếu một tập tin đã bị thay đổi tính từ thời điểm được sinh ra từ .git (check out) và chưa đưa vào staging area thì được xem là modified.

6.4 Bài tập và câu hỏi

Bài tập 6a. Quy trình làm việc và các trạng thái của tập tin trong Git.

Mục tiêu bài tập

- Thực hành quy trình: Thư mục làm việc (Working directory) > Khu tạm (Staging area) > Kho chứa (Repository)

- Phân biệt các trạng thái của tập tin: Chưa được theo dõi (Untracked), Đã được theo dõi (Tracked/Staged), và Đã lưu (Committed)

- Sử dụng thành thạo các lệnh: git init, git status, git add, git commit và git rm --cached

Các bước thực hiện

Bước 1: Khởi tạo dự án và Kho chứa

- Tạo một thư mục mới tên là LabGit trên máy tính

- Mở cửa sổ dòng lệnh (CMD), di chuyển vào thư mục LabGit

- Nhập lệnh để biến thư mục này thành một kho chứa Git

git init

- Kết quả: Xuất hiện thư mục ẩn .git. Đây chính là Kho chứa

Bước 2: Thao tác tại Thư mục làm việc (Trạng thái Untracked)

- Tạo một tập tin mới tên là readme.txt với nội dung bất kỳ

- Dùng lệnh kiểm tra trạng thái:

git status

- Quan sát: Git sẽ báo tập tin readme.txt ở trạng thái Untracked files (chưa được theo dõi) vì nó mới chỉ nằm ở Thư mục làm việc.

Bước 3: Đưa tập tin vào Khu tạm (Trạng thái Staged)

- Sử dụng lệnh sau để đánh dấu tập tin chuẩn bị lưu phiên bản:

git add readme.txt

- Kiểm tra lại trạng thái bằng git status.

- Quan sát: Tập tin bây giờ nằm trong mục Changes to be committed. Trạng thái này gọi là Staged (đã nằm trong Khu tạm/index).

Bước 4: Trải nghiệm việc gỡ bỏ khỏi Khu tạm

- Giả sử bạn chưa muốn lưu tập tin này vào kho, hãy đưa nó quay lại trạng thái chưa theo dõi:

git rm --cached readme.txt

- Kiểm tra git status. Tập tin sẽ quay lại trạng thái Untracked.

- Thực hiện git add readme.txt một lần nữa để tiếp tục bài tập

Bước 5: Lưu phiên bản vào Kho chứa (Trạng thái Committed)

- Thực hiện lệnh commit để lưu vĩnh viễn thay đổi vào Kho chứa:

git commit -m "Khoi tao du an voi tap tin readme"

- Kiểm tra git status

- Quan sát: Git báo "nothing to commit, working tree clean". Tập tin đã ở trạng thái Committed (đã lưu vào Kho chứa).

Bài tập 6b. Rèn kỹ năng phân tích

Ở phần này, thay vì chỉ làm theo hướng dẫn, bạn phải quan sát sự khác biệt giữa các trạng thái.

Tình huống 1: Chỉnh sửa tập tin đã Commit

- Mở lại tập tin readme.txt, thêm một dòng nội dung mới và lưu lại

- Chạy lệnh git status

- Câu hỏi phân tích: Tại sao tập tin này lại xuất hiện ở mục "Changes not staged for commit" mà không phải "Untracked"? Sự khác biệt giữa một tập mới tạo và một tập tin đã từng được commit nhưng bị sửa đổi là gì?

Tình huống 2: So sánh nội dung (git diff)

- Sử dụng lệnh git diff để xem sự thay đổi

- Câu hỏi phân tích: Lệnh này đang so sánh nội dung giữa khu vực nào với khu vực nào? (Thư mục làm việc với Khu tạm hay Thư mục làm việc với Kho chứa?).

Bài tập 6c. Rèn kỹ năng tổng hợp: "Thiết kế quy trình xử lý sự cố"

Bạn đóng vai trò là một trưởng nhóm kỹ thuật để đưa ra giải pháp cho các tình huống sau.

Tình huống 1: "Lỡ tay đưa nhầm tập tin quan trọng"

- Giả lập: Bạn tạo một tập tin tên là matkhau.txt (giả sử chứa mật khẩu cá nhân). Bạn vô tình gõ git add . (đưa tất cả vào khu tạm)

- Yêu cầu: Hãy tìm cách đưa tập tin matkhau.txt ra khỏi Khu tạm mà không được xóa tập tin đó khỏi Thư mục làm việc

- Tư duy tổng hợp: Viết ra quy trình các lệnh cần dùng và giải thích tại sao chọn cách đó thay vì xóa tập tin đi làm lại

Tình huống 2: "Khôi phục trạng thái cũ"

- Giả lập: Sau khi sửa tập tin readme.txt ở Bài tập 6.b (Tình huống 1), bạn nhận ra nội dung mới vừa sửa bị sai hoàn toàn và muốn quay về nội dung của lần Commit gần nhất

- Yêu cầu: Tìm lệnh để "đè" nội dung từ Kho chứa ngược trở lại Thư mục làm việc để xóa bỏ những sửa đổi sai lầm

- Tư duy tổng hợp: Kết hợp kiến thức về 3 khu vực để giải thích: Tại sao Kho chứa (Repository) được coi là “nguồn tin cậy” khi Thư mục làm việc xảy ra lỗi?

Bài tập 6d. Rèn tư duy suy luận. Dựa trên kiến thức về 3 khu vực (Working Directory, Staging Area, Repository). Giải thích:

- Nếu người A sửa tập tin nhưng chưa git add, người B có thấy được thay đổi đó trong Kho chứa không?

- Tại sao Git lại cần "Khu tạm" (Staging Area) mà không cho phép Commit thẳng từ Thư mục làm việc?

Bài sau: