Merge conflicts (xung đột)
Xem thửVấn đề cần giải quyết
Bạn chạy git merge feature và màn hình báo CONFLICT (content): Merge conflict in app.js. Tệp của bạn đầy ký tự lạ <<<<<<<. Nhiều người lần đầu thấy cảnh này đều hoảng và nghĩ mình làm hỏng kho. Thực ra không có gì hỏng: Git chỉ dừng lại vì nó không thể tự quyết định nên giữ bản nào, và nhờ bạn.
Sau bài này, bạn có thể
- Giải thích khi nào xung đột xảy ra và khi nào không.
- Đọc ba dòng dấu của Git trong tệp xung đột.
- Giải quyết xung đột từ đầu đến cuối, hoặc hủy bằng
--abort.
Tiên quyết: bài git merge.
Khi nào có xung đột
Xung đột xảy ra khi hai nhánh cùng sửa một vùng của một tệp theo cách khác nhau. Nếu hai bên sửa hai vùng khác nhau, Git tự gộp được. Điều đáng nhớ: xung đột không xảy ra chỉ vì có hai nhánh, mà vì hai bên sửa cùng một chỗ.
Đọc các dấu trong tệp
Cách đọc hình. Git chèn ba dòng dấu để chia tệp theo bên. Phần trên ======= là của nhánh hiện tại (HEAD), phần dưới là của nhánh đang được merge. Kết quả cuối cùng do bạn viết: giữ một bên hoặc kết hợp cả hai, rồi xóa cả ba dòng dấu. Để sót dấu rồi commit nghĩa là commit nội dung hỏng.
Thử và quan sát
Hãy dự đoán: hai nhánh cùng sửa một dòng. Sau khi bạn sửa tệp và xóa các dấu, bạn cần chạy những lệnh nào để hoàn tất merge? Chạy mô phỏng toàn bộ quy trình, rồi thử biến thể bỏ cuộc bằng --abort.
Đang tải Animation…
Bảng bước (bản đọc tĩnh của mô phỏng)
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git merge feature | Cả hai nhánh sửa cùng một dòng nên Git không tự chọn được. Nó dừng lại và chèn ba dòng dấu vào tệp. |
| 2 | git status | Tệp nằm trong mục Unmerged paths: both modified. |
| 3 | (sửa tệp trong trình soạn thảo) | Bạn chọn nội dung đúng (ở đây kết hợp cả hai) và xóa cả ba dòng dấu. Để sót dấu rồi commit sẽ commit nội dung hỏng. |
| 4 | git add app.js | Add báo cho Git rằng xung đột trong tệp đã được xử lý. |
| 5 | git commit | Commit hoàn tất merge: M có hai cha là C3 và F1 và chứa nội dung bạn đã chọn. |
Biến thể: Bỏ cuộc bằng --abort
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git merge feature | Gặp xung đột như trước: tệp chứa các dấu và merge đang dở. |
| 2 | git merge --abort | Hủy merge đang dở: tệp và lịch sử quay về đúng trạng thái trước khi merge. |
| 3 | git status | Không còn dấu vết của merge; bạn có thể suy nghĩ lại hoặc thử cách khác. |
Giải đáp. Hai lệnh: git add app.js (báo rằng xung đột trong tệp đã xử lý) rồi git commit để tạo commit hợp nhất. Nếu bạn đang rebase thay vì merge, thay git commit bằng git rebase --continue. Ở biến thể, git merge --abort đưa bạn về đúng trạng thái trước merge mà không mất gì.
Nhận biết và công cụ
git merge feature
git status
git mergetool
git merge --abort
git status liệt kê tệp ở mục "Unmerged paths"; git mergetool mở công cụ merge trực quan đã cấu hình; --abort bỏ cuộc.
Giảm xung đột từ gốc
- Giữ nhánh ngắn hạn và merge hoặc pull thường xuyên.
- Chia tính năng thành các phần nhỏ.
- Thống nhất định dạng code (formatter) để tránh xung đột do khoảng trắng.
Hiểu lầm thường gặp
- "Có xung đột nghĩa là ai đó làm sai." Nó chỉ nghĩa là hai người sửa cùng một chỗ.
- "git add là xong merge." Add chỉ đánh dấu đã giải quyết; còn phải commit (hoặc continue).
- "Chọn một bên là đủ." Đôi khi kết hợp cả hai mới đúng; luôn chạy thử trước khi commit.
Tự kiểm tra
- Phần nào của tệp xung đột thuộc về nhánh hiện tại?
- Vì sao phải xóa cả ba dòng dấu trước khi commit?
- Bạn bối rối giữa chừng. Lệnh nào đưa bạn về trước merge?
Đáp án gợi ý. (1) Phần trên dòng ======= (từ <<<<<<< HEAD). (2) Nếu để sót, dấu trở thành một phần nội dung tệp và làm hỏng mã. (3) git merge --abort.
Điều cần nhớ
- Xung đột xảy ra khi hai bên sửa cùng một vùng.
- Quy trình: sửa tệp, xóa dấu,
git add,git commit(hoặcrebase --continue). --abortluôn là lối thoát an toàn.- Bài sau: các chiến lược merge.
Nguồn tham khảo
Pro Git, chương 3.2 "Basic Merge Conflicts" (git-scm.com/book).