Git nội bộ: hiểu Git từ bên trong
Mỗi bài mổ xẻ một cơ chế bên trong Git bằng lệnh git thật và nội dung thư mục .git — không chỉ học lệnh mà hiểu vì sao Git hoạt động như vậy.
18/18 phần đã đăng
Công cụ
1
Git là một cơ sở dữ liệu nội dung địa chỉ hoá: blob, tree, commit
Đa số người dùng Git như một hộp đen với add/commit/push. Nhưng bên dưới, Git chỉ là một key-value store đơn giản: khoá là SHA của nội dung, giá trị là object. Bài này mổ xẻ ba loại object (blob, tree, commit) bằng lệnh git thật — tự tính ra khoá SHA của Git và chứng minh nó băm nội dung, không phải tên file.
22/09/2026
· 6 phút đọc
2
Ba vùng của Git: working directory, index (staging) và repository
Ai cũng gõ git add rồi git commit, nhưng ít người biết index (staging) thực chất là gì. Bài này mổ xẻ ba vùng của Git bằng lệnh thật: index là một file nhị phân lưu SHA blob + mode + path, git add ghi blob mới và cập nhật index, và hai lệnh diff soi hai khe khác nhau — hiểu điều này giúp bạn đọc đúng git status và dùng đúng git reset.
22/09/2026
· 6 phút đọc
3
Ref, HEAD và branch trong Git: một nhánh chỉ là một con trỏ 41 byte
Nhiều người tưởng tạo branch là sao chép cả dự án, nên ngại tạo nhánh. Thực ra một branch chỉ là một file 41 byte chứa SHA của commit. Bài này chứng minh bằng lệnh thật: HEAD trỏ tới ref, ref trỏ tới commit, tạo branch = ghi 41 byte, chuyển branch = đổi con trỏ HEAD — và vì sao Git khuyến khích bạn tạo nhánh thoải mái.
22/09/2026
· 5 phút đọc
4
Lịch sử Git là một DAG: commit trỏ cha, và lưu snapshot chứ không lưu diff
Nhiều người tưởng Git lưu 'các thay đổi' (diff) giữa các commit. Thực ra mỗi commit lưu một ảnh chụp (snapshot) toàn bộ cây, và trỏ tới commit cha — tạo thành một đồ thị có hướng không chu trình (DAG). Bài này chứng minh bằng lệnh thật: file không đổi dùng lại cùng blob qua nhiều commit, và merge tạo commit hai cha.
22/09/2026
· 6 phút đọc
5
Merge ba chiều trong Git: base, ours, theirs — và vì sao có xung đột
Nhiều người sợ merge vì không hiểu Git làm gì. Thực ra merge của Git là 'ba chiều': nó tìm commit tổ tiên chung (merge base) rồi so với hai phía để biết bên nào đã đổi gì. Bài này chứng minh bằng lệnh thật: thay đổi khác vùng tự gộp, thay đổi cùng dòng gây xung đột, và index giữ cả ba phiên bản (base/ours/theirs).
22/09/2026
· 5 phút đọc
6
Rebase trong Git: vì sao nó viết lại lịch sử — chứng minh bằng SHA thật
Nhiều người dùng rebase mà không hiểu nó khác merge chỗ nào, rồi hoảng khi thấy commit 'biến mất'. Sự thật: rebase KHÔNG di chuyển commit cũ — nó tạo commit MỚI với SHA khác vì đổi parent. Bài này chứng minh bằng lệnh thật: cùng nội dung, cùng message, nhưng SHA đổi hoàn toàn; lịch sử thẳng thay vì rẽ nhánh; và commit cũ vẫn nằm trong reflog để cứu.
22/09/2026
· 6 phút đọc
7
Reflog trong Git: vì sao gần như không thao tác nào làm mất commit vĩnh viễn
Nhiều người tưởng git reset --hard hay xoá nhánh là mất commit không cứu được, rồi hoảng loạn. Sự thật: Git ghi mọi lần HEAD dịch chuyển vào reflog, và commit mồ côi còn nằm trong kho hàng tuần trước khi bị gc dọn. Bài này khôi phục thật một reset --hard làm 'mất' hai commit và một nhánh đã xoá — chỉ bằng SHA đọc từ reflog.
22/09/2026
· 6 phút đọc
8
Packfile trong Git: vì sao mỗi commit là snapshot mà .git không phình vô hạn
Git lưu snapshot đầy đủ mỗi commit chứ không lưu diff — nghe như thư mục .git phải nhân đôi mỗi lần sửa file lớn. Thực tế không. Bài này đo thật: 6 phiên bản của một file 622 KB, git gc gói lại chỉ còn 34 KB nhờ packfile nén delta. Mở verify-pack xem tận mắt chuỗi delta depth 1 đến 5, mỗi bản sửa chỉ tốn ~350 byte.
22/09/2026
· 6 phút đọc
9
git status và git diff so cái gì với cái gì — và cái bẫy .gitignore ai cũng dính
Bạn gõ git status và git diff hàng chục lần mỗi ngày nhưng ít khi hỏi chúng so những gì. Câu trả lời nằm ở ba vùng: working tree, index, HEAD. Bài này đo thật: git diff so working với index, git diff --cached so index với HEAD, git status --short đọc bằng hai cột, và vì sao thêm file vào .gitignore không gỡ được file đã theo dõi.
22/09/2026
· 6 phút đọc
10
git bisect: tìm commit gây lỗi trong 5 lần thay vì 30 — và cách để máy tự làm
Một tính năng vừa hỏng mà bạn không biết commit nào gây ra, giữa hàng trăm commit. Dò tuần tự là ác mộng. git bisect tìm nhị phân: mỗi lần chia đôi số commit khả nghi. Bài này đo thật trên 31 commit — bisect tìm ra thủ phạm chỉ sau 5 lần kiểm thử, và git bisect run để máy tự chạy hết bằng một script.
22/09/2026
· 6 phút đọc
11
cherry-pick và detached HEAD: bê một commit lẻ, và vì sao HEAD rời không đáng sợ
Bạn chỉ muốn một commit hotfix từ nhánh tính năng sang main, không phải cả nhánh — đó là cherry-pick. Và trạng thái detached HEAD mà bisect/checkout hay đẩy bạn vào khiến nhiều người hoảng. Bài này đo thật: cherry-pick tạo commit mới cùng nội dung khác SHA, và một commit tạo trong detached HEAD mất hay được cứu tùy bạn có đặt tên nhánh hay không.
22/09/2026
· 6 phút đọc
12
git fetch và git push trao đổi gì với máy chủ: want, have, và pack protocol
Push và fetch không gửi toàn bộ repo mỗi lần — chúng thương lượng để chỉ truyền object còn thiếu. Bài cuối sê-ri Git nội bộ mổ xẻ giao thức: ref advertisement, đàm phán want/have, rồi pack protocol gói phần thiếu. Đo thật bằng GIT_TRACE_PACKET — client nói want một SHA, have những SHA đã có, server chỉ gửi packfile chênh lệch. Và vì sao push đôi khi bị từ chối.
22/09/2026
· 6 phút đọc
13
git stash: cất tạm việc dở để đổi nhánh gấp — và khác biệt pop vs apply
Đang sửa dở nửa chừng thì sếp bảo fix bug gấp ở nhánh khác — commit thì chưa xong, bỏ thì mất. git stash cất thay đổi vào ngăn tạm để working tree sạch lại, làm việc khác rồi lấy ra tiếp. Bài này đo thật bằng git 2.39: stash bỏ qua file untracked (phải -u), apply giữ stash còn pop thì xóa, và bản chất stash là commit ẩn.
22/09/2026
· 4 phút đọc
14
git worktree: một kho .git, nhiều thư mục làm việc song song
Cần code nhánh A mà vẫn build/test nhánh B cùng lúc? Stash làm mất ngữ cảnh, clone lại thì tốn dung lượng và tách rời kho. git worktree cho một .git dùng chung nhưng nhiều thư mục làm việc, mỗi cái ở một nhánh. Bài này đo thật: hai cây hai nhánh làm việc độc lập, commit ở cây này cây kia thấy ngay, và vì sao không checkout được cùng một nhánh ở hai cây.
22/09/2026
· 5 phút đọc
15
git submodule: repo lồng trong repo — vì sao nó ghim commit và hay gây rối
Submodule nhúng một kho Git vào kho khác nhưng không chép file — nó chỉ ghi một con trỏ tới đúng một commit của kho con. Điều này khiến build tái lập được nhưng cũng gây rối nếu không hiểu. Bài này đo thật: submodule là gitlink mode 160000, lib lên v2 mà app vẫn ghim v1, và cách cập nhật đúng — cùng ba cái bẫy kinh điển.
22/09/2026
· 5 phút đọc
16
Git hooks: tự động kiểm tra trước commit/push — và vì sao hook không clone theo
Git có thể tự chạy script của bạn ở mỗi commit, mỗi push — chặn code lỗi, ép định dạng message, chạy test trước khi push. Bài này đo thật bằng git 2.39: pre-commit chặn commit chứa TODO, commit-msg bắt buộc mã ticket, và cái bẫy lớn nhất — hook nằm trong .git nên không clone theo, đồng đội không có.
22/09/2026
· 5 phút đọc
17
Rebase tương tác: gộp, sửa, xóa commit — dọn lịch sử nhánh cho sạch
Nhánh tính năng thường đầy commit fix typo, wip, sua nua — mở PR với lịch sử đó thì reviewer khó đọc. git rebase -i cho bạn viết lại N commit gần nhất: squash/fixup gộp, reword sửa message, drop xóa, đổi thứ tự. Bài này đo thật bằng git 2.39, tự động hóa qua GIT_SEQUENCE_EDITOR: 5 commit lộn xộn gộp còn 2, nội dung nguyên vẹn.
22/09/2026
· 5 phút đọc
18
git gc, fsck và bảo trì kho: dọn rác, nén, và vì sao reflog cứu object khỏi bị xóa
Thư mục .git tự phình rồi tự dọn. Bài cuối sê-ri Git đo thật: gc gom loose object thành packfile nén (9 object 36 KiB xuống 1.86 KiB), fsck báo object mồ côi, và điểm nhiều người hiểu sai — gc --prune=now KHÔNG xóa object mà reflog còn giữ; phải hết hạn reflog rồi gc mới xóa thật. Đây chính là lưới an toàn cứu commit tưởng đã mất.
22/09/2026
· 5 phút đọc