Ở Bài 3 chúng ta tách nhánh ra để làm việc song song. Bài này khép lại vòng lặp: đưa 2 dòng lịch sử đã phân kỳ hợp nhất lại. Điều thú vị là Git không so sánh 2 nhánh trực tiếp với nhau — nó cần một "trọng tài" thứ ba: điểm mà 2 nhánh từng tách ra.
1. Vì Sao Cần "Ba Bên" Để Merge? (Merge Base)
Giả sử main và feature đều sửa dòng cuối của 1 file, nhưng theo 2 cách khác
nhau. Nếu chỉ so sánh trực tiếp main với feature, Git chỉ biết
2 bản khác nhau — không thể biết bên nào mới thực sự "thay đổi" so với ban đầu. Git
cần điểm thứ ba: merge base — commit tổ tiên chung gần nhất, tức chính xác nơi 2
nhánh tách ra. Có base, Git mới trả lời được: "so với base, main đổi gì, và
feature đổi gì?" — đây gọi là three-way merge.
$ git merge-base main feature
a1b2c3d # hash của commit tổ tiên chung gần nhất — nơi 2 nhánh tách ra
2. Fast-Forward vs Merge Commit
Nhắc lại fast-forward từ Bài 3: nếu merge base
trùng với commit hiện tại của main (tức main chưa hề đổi gì
kể từ lúc tách nhánh), Git chỉ cần di chuyển con trỏ — không tạo commit mới. Nếu
merge base khác cả 2 đầu nhánh (cả 2 bên đều có commit mới kể từ lúc tách), Git bắt
buộc phải tạo 1 merge commit — commit đặc biệt duy nhất có
2 commit cha thay vì 1.
3. Three-Way Merge: So Sánh Base, Ours, Theirs
Với mỗi phần nội dung (Git làm việc ở cấp độ dòng), thuật toán so sánh 3 phiên bản — base (gốc chung), ours (HEAD hiện tại), theirs (nhánh đang merge vào) — và áp dụng đúng 4 quy tắc:
| So với base | Kết quả |
|---|---|
| Cả ours và theirs đều KHÔNG đổi | Giữ nguyên nội dung base |
| Chỉ ours đổi, theirs không đổi | Lấy bản ours |
| Chỉ theirs đổi, ours không đổi | Lấy bản theirs |
| Cả 2 đều đổi, và đổi GIỐNG NHAU | Lấy bản đó (không xung đột) |
| Cả 2 đều đổi, nhưng đổi KHÁC NHAU | XUNG ĐỘT (conflict) — cần con người quyết định |
4. Khi Xung Đột Phát Sinh: Git Đánh Dấu Ngay Trong File
Khi rơi vào trường hợp cuối, Git không đoán mò — nó ghi cả 2 phiên bản thẳng vào file trên Working Directory, kẹp giữa các dấu đánh dấu xung đột, rồi dừng lại chờ bạn quyết định:
<<<<<<< HEAD (main)
const version = "v2-main";
=======
const version = "v2-feature";
>>>>>>> feature
Đồng thời, Staging Index (Bài 2) tạm thời lưu 3 phiên bản song song của file đó (gọi là stage 1/2/3: base/ours/theirs) thay vì 1 bản duy nhất — đây là cách Git "nhớ" đủ thông tin để bạn (hoặc công cụ merge tool) tra cứu lại nếu cần trong lúc giải quyết.
<<<<<<</=======/>>>>>>>
chỉ là văn bản thường được Git chèn vào — nếu bạn git add rồi commit mà quên xoá, chúng
sẽ nằm nguyên trong code, thường gây lỗi cú pháp (với code) hoặc dữ liệu rác (với file khác). Luôn
build/chạy lại code sau khi giải quyết xung đột, trước khi commit.
5. Giải Quyết Xung Đột: Sửa, Add, Rồi Commit
Giải quyết chỉ đơn giản là mở file, xoá 3 dòng đánh dấu, giữ lại đúng nội dung bạn muốn (có thể là bản
ours, bản theirs, hoặc kết hợp cả 2), rồi git add để báo "xung đột này đã giải quyết
xong" — git commit lúc này sẽ tự tạo merge commit với message mặc định
kiểu "Merge branch 'feature' into main".
git log hay công cụ blame vẫn truy ngược được chính xác dòng code nào tới từ nhánh nào.
Nếu merge commit chỉ có 1 cha (giống commit thường), Git sẽ "quên" mất rằng đã từng có 2 dòng phát
triển song song — đây chính là điểm khác biệt cấu trúc duy nhất giữa merge commit và commit thường,
không phải nội dung mà nó lưu.
Sân chơi tương tác: Merge & Conflict Simulator
Tiếp nối engine đồ thị commit từ Bài 3, giờ hỗ trợ thêm
git merge. Tạo 2 nhánh, sửa "nội dung file" khác nhau trên mỗi nhánh rồi commit, sau đó
git merge để thử cả 2 kịch bản: hợp nhất êm đẹp và xung đột thật sự.
Terminal giả lập
git branch <tên>git checkout <tên>git commit -m "..."git merge <tên>git log
Nhật ký
Trắc nghiệm ôn tập
Câu 1: Vì sao Git cần biết "merge base" (tổ tiên chung) thay vì chỉ so sánh trực tiếp 2 branch với nhau?
Trắc nghiệm ôn tập
Câu 2: ours đã thay đổi so với base, nhưng theirs giống hệt base. Kết quả
three-way merge là gì?
Trắc nghiệm ôn tập
Câu 3: Điều gì làm merge commit khác biệt về CẤU TRÚC so với 1 commit thường?