Sửa lỗi nhanh TL;DR
Gặp xung đột hợp nhất? Cách nhanh nhất để giải quyết là sửa thủ công các phần xung đột trong tệp của bạn. Dưới đây là hướng dẫn nhanh:
- **Kiểm tra trạng thái:**
git status
Lệnh này cho bạn biết tệp nào đang ở trạng thái xung đột. Bạn sẽ tìm thấy chúng được liệt kê dưới mục "Unmerged paths."
- **Mở và chỉnh sửa:** Mở từng tệp bị xung đột trong trình soạn thảo văn bản yêu thích của bạn. Git đánh dấu các xung đột bằng các ký hiệu đặc biệt, như sau:
<<<<<<< HEAD
// Mã từ nhánh hiện tại của bạn (HEAD)
=======
// Mã từ nhánh bạn đang hợp nhất
>>>>>>> branch-name
Quyết định giữ thay đổi nào, hoặc kết hợp chúng khi cần. Sau đó, điều quan trọng là phải xóa các ký hiệu <<<<<<<, ======= và >>>>>>>.
- **Đánh dấu là đã giải quyết:** Sau khi chỉnh sửa một tệp và xóa tất cả các ký hiệu xung đột, hãy báo cho Git biết bạn đã sửa xung đột cho tệp đó:
git add file.txt
Lặp lại bước này cho mỗi tệp bị xung đột.
- **Hoàn tất hợp nhất:** Khi tất cả các xung đột đã được giải quyết và được thêm vào staging, hãy hoàn tất việc hợp nhất bằng một commit:
git commit -m "Giải quyết xung đột hợp nhất trong file.txt"
Git thường điền sẵn một thông báo commit hợp nhất cho bạn. Bạn có thể chấp nhận hoặc sửa đổi nó.
Nguyên nhân chi tiết: Tại sao xung đột hợp nhất xảy ra
Lỗi CONFLICT (content): Merge conflict in file.txt xuất hiện khi Git không thể tự động tìm ra cách kết hợp các thay đổi được thực hiện trên cùng một dòng trong cùng một tệp giữa hai nhánh khác nhau mà bạn đang cố gắng hợp nhất.
Hãy cùng tìm hiểu lý do tại sao điều này xảy ra:
- **Lịch sử phân kỳ:** Hãy tưởng tượng bạn và một đồng đội (hoặc bạn đang làm việc trên một tính năng riêng biệt) đều bắt đầu từ cùng một điểm (một commit chung). Cả hai bạn đều thực hiện các thay đổi khác nhau, và khi bạn cố gắng đưa các thay đổi đó lại với nhau, lịch sử dự án của bạn đã phân kỳ.
- **Cùng dòng, thay đổi khác nhau:** Nguyên nhân phổ biến nhất là khi cả hai nhánh cùng sửa đổi *chính xác các dòng* trong một tệp. Điều này cũng xảy ra nếu một nhánh xóa các dòng mà nhánh kia đã sửa đổi. Trong những trường hợp này, Git cần con người quyết định thay đổi nào nên được ưu tiên. Git thường đủ thông minh để tự động hợp nhất các thay đổi ở các phần khác nhau của tệp, nhưng khi không rõ ràng, nó sẽ đánh dấu một xung đột.
- **Hợp nhất ba chiều:** Git sử dụng chiến lược "hợp nhất ba chiều" (three-way merge). Nó so sánh tổ tiên chung của hai nhánh với phiên bản mới nhất của cả hai nhánh. Nếu cùng một phần của tệp đã được thay đổi khác nhau ở cả hai nhánh kể từ tổ tiên chung đó, thì xung đột sẽ phát sinh. Đây là lúc Git yêu cầu bạn nhập liệu vì nó không thể tự quyết định.
Khi Git phát hiện xung đột, nó sẽ tạm dừng quá trình hợp nhất. Nó để các tệp có vấn đề ở trạng thái đặc biệt, đánh dấu rõ ràng các phần xung đột bằng các ký hiệu quen thuộc <<<<<<<, ======= và >>>>>>>. Đây là dấu hiệu để bạn can thiệp và giải quyết thủ công những điểm không rõ ràng này.
Khắc phục xung đột: Các tùy chọn của bạn
Giải quyết xung đột hợp nhất Git có thể được thực hiện theo một vài cách chính: chỉnh sửa thủ công hoặc sử dụng công cụ hợp nhất chuyên dụng.
Cách 1: Giải quyết thủ công trong trình soạn thảo văn bản (Cách trực tiếp)
Phương pháp này thường là nhanh nhất và trực tiếp nhất, đặc biệt đối với các xung đột nhỏ hơn, đơn giản.
- **Xác định các tệp bị xung đột:**
Sau khi bắt đầu hợp nhất (ví dụ: git merge feature-branch) và thấy thông báo xung đột, hãy chạy git status. Đây là lệnh bạn cần dùng để hiểu rõ tình hình.
git status
Bạn sẽ thấy đầu ra tương tự như sau:
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abandon merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: file.txt
Thông báo này cho bạn biết rõ rằng file.txt hiện đang gặp xung đột.
- **Chỉnh sửa tệp bị xung đột:**
Bây giờ, hãy mở file.txt trong trình soạn thảo văn bản yêu thích của bạn. Bên trong, bạn sẽ thấy các ký hiệu xung đột trông như ví dụ này:
// Một số mã được chia sẻ không thay đổi
<<<<<<< HEAD
// Đây là nội dung từ nhánh hiện tại của bạn (HEAD).
// Có thể bạn đã thêm một hàm mới hoặc sửa lỗi ở đây.
=======
// Đây là nội dung từ nhánh bạn đang hợp nhất (ví dụ: 'feature-branch').
// Một đồng nghiệp có thể đã triển khai logic khác ở đây.
>>>>>>> feature-branch
// Thêm mã được chia sẻ không thay đổi
`<<<<<<< HEAD`: Điều này đánh dấu sự bắt đầu của các thay đổi từ nhánh hiện đang hoạt động của bạn.
- `=======`: Dòng này hoạt động như một dải phân cách, tách các thay đổi của bạn khỏi các thay đổi đang đến.
- `>>>>>>> branch-name`: Điều này cho biết kết thúc của các thay đổi đến từ nhánh bạn đang hợp nhất.
Công việc của bạn là xem xét cẩn thận các phần này. Bạn cần quyết định:
- Nên giữ phiên bản nào?
- Bạn có nên kết hợp các phần từ cả hai phiên bản không?
- Có cách nào hoàn toàn mới, tốt hơn để tích hợp cả hai tập hợp thay đổi không?
Hãy xem xét một Ví dụ về cách giải quyết:
Giả sử file.txt ban đầu chứa:
function greet() {
return "Hello";
}
Và nhánh HEAD của bạn đã sửa đổi nó thành:
function greet() {
return "Hello, World!";
}
Trong khi đó, feature-branch bạn đang hợp nhất đã thay đổi nó thành:
function greet() {
return "Hi!";
}
Tệp bị xung đột sau đó sẽ xuất hiện như sau:
function greet() {
<<<<<<< HEAD
return "Hello, World!";
=======
return "Hi!";
>>>>>>> feature-branch
}
Bạn có thể giải quyết vấn đề này bằng cách chọn giữ "Hello, World!", hoặc bằng cách tạo một thông báo mới, kết hợp. Ví dụ, để giữ "Hello, World!":
function greet() {
return "Hello, World!";
}
Hoặc, nếu logic cho phép một cách tiếp cận kết hợp:
function greet() {
// Ở đây, bạn sẽ quyết định lời chào nào có ý nghĩa, hoặc kết hợp cho logic ứng dụng cụ thể
return "Chào mừng, đồng nghiệp lập trình viên!";
}
Tuyệt đối quan trọng: bạn phải xóa tất cả các dòng <<<<<<<, ======= và >>>>>>>. Git sẽ không cho phép bạn commit cho đến khi các ký hiệu này bị xóa.
- **Thêm tệp đã giải quyết vào staging:**
Sau khi bạn đã chỉnh sửa thủ công file.txt và xóa tất cả các ký hiệu xung đột, hãy lưu các thay đổi của bạn. Sau đó, báo cho Git biết xung đột của tệp này đã được giải quyết bằng cách thêm nó vào khu vực staging:
git add file.txt
Chạy git status một lần nữa. Bây giờ bạn sẽ thấy file.txt được liệt kê dưới "Changes to be committed" thay vì "Unmerged paths." Điều này xác nhận rằng nó đã sẵn sàng cho bước tiếp theo.
- **Commit hợp nhất:**
Với tất cả các tệp bị xung đột đã được giải quyết và thêm vào staging, hoàn tất quá trình hợp nhất bằng cách tạo một commit hợp nhất:
git commit
Lệnh này sẽ khởi chạy trình soạn thảo văn bản mặc định của bạn, được điền sẵn thông báo commit hợp nhất. Thông báo này thường giải thích những nhánh nào đã được hợp nhất và ghi chú rằng các xung đột đã được giải quyết. Bạn có thể tùy chỉnh thông báo này nếu muốn, sau đó lưu và đóng trình soạn thảo để hoàn tất commit.
Cách 2: Sử dụng công cụ hợp nhất trực quan (Trợ giúp đồ họa)
Khi các xung đột trở nên phức tạp, hoặc nếu bạn chỉ đơn giản là thích giao diện đồ họa hơn, Git cung cấp hỗ trợ mạnh mẽ cho các công cụ hợp nhất bên ngoài.
- **Cấu hình công cụ hợp nhất (nếu bạn chưa làm):**
Bạn có thể thiết lập Git để sử dụng các công cụ phổ biến như Beyond Compare, KDiff3, Meld, hoặc thậm chí các tính năng hợp nhất tích hợp sẵn của IDE của bạn (như VS Code). Ví dụ, để tích hợp VS Code làm công cụ hợp nhất ưu tiên của bạn:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'
git config --global mergetool.vscode.trustExitCode false
Lưu ý: Các lệnh chính xác có thể hơi khác nhau tùy thuộc vào công cụ và hệ điều hành bạn đã chọn. Luôn tham khảo tài liệu của công cụ bạn để biết hướng dẫn thiết lập chính xác.
- **Khởi chạy công cụ hợp nhất:**
Khi công cụ của bạn đã được cấu hình và Git phát hiện xung đột, chỉ cần chạy:
git mergetool
Git sau đó sẽ đi qua từng tệp bị xung đột, khởi chạy công cụ hợp nhất đã cấu hình của bạn từng tệp một. Thông thường, công cụ sẽ hiển thị cho bạn ba hoặc bốn bảng điều khiển: tổ tiên chung, các thay đổi của bạn và các thay đổi đang đến. Một số công cụ cũng hiển thị kết quả hợp nhất cuối cùng. So sánh trực quan này giúp bạn dễ dàng chọn các phần bạn muốn giữ hoặc kết hợp hơn nhiều.
- **Lưu và thoát công cụ:**
Sau khi bạn đã giải quyết xung đột trực quan trong công cụ hợp nhất, hãy lưu các thay đổi của bạn và đóng công cụ. Git sẽ tự động thêm tệp vào staging, đánh dấu nó là đã giải quyết trong quá trình này.
- **Commit hợp nhất:**
Cũng như với giải quyết thủ công, khi tất cả các xung đột đã được giải quyết (thủ công hoặc thông qua công cụ), hãy hoàn tất việc hợp nhất bằng cách chạy git commit.
Cách 3: Hủy bỏ hợp nhất (Khi mọi thứ trở nên tồi tệ)
Đôi khi, các xung đột có vẻ quá sức, hoặc bạn có thể nhận ra mình đã vội vàng hợp nhất. Trong những trường hợp như vậy, bạn có thể an toàn hủy bỏ thao tác hợp nhất, đưa kho lưu trữ của bạn trở lại trạng thái trước khi bạn bắt đầu hợp nhất.
- **Hủy bỏ hợp nhất:**
git merge --abort
Lệnh mạnh mẽ này dừng quá trình hợp nhất ngay lập tức. Nó đặt lại thư mục làm việc và chỉ mục Git của bạn chính xác như trước khi bạn gọi git merge. Quan trọng: Hãy lưu ý rằng bất kỳ thay đổi chưa được commit nào trong thư mục làm việc của bạn có thể bị mất nếu chúng xung đột với trạng thái mà Git khôi phục. Luôn là một thực hành tốt để commit hoặc stash công việc của bạn trước khi thử hợp nhất.
Các bước xác minh: Xác nhận việc hợp nhất của bạn
Sau khi giải quyết thành công xung đột hợp nhất và tạo commit hợp nhất quan trọng đó, dành một vài khoảnh khắc để xác minh mọi thứ đều ổn là một thực hành thông minh. Điều này đảm bảo công việc khó khăn của bạn không gây ra vấn đề mới.
- **Kiểm tra trạng thái Git:**
Bước đầu tiên của bạn phải luôn là xác nhận trạng thái của kho lưu trữ của bạn:
git status
Một đầu ra đáng tin cậy như On branch main nothing to commit, working tree clean có nghĩa là việc hợp nhất của bạn đã thành công và không có thay đổi còn sót lại hoặc xung đột chưa được giải quyết.
- **Xem lại lịch sử Commit:**
Tiếp theo, hãy xem lịch sử commit của bạn. Điều này đảm bảo commit hợp nhất đã được tạo đúng cách và lịch sử phản ánh chính xác cách các thay đổi đã được tích hợp:
git log --oneline --graph --all
Lệnh này cung cấp một cái nhìn tổng quan, đồ họa về lịch sử commit của bạn. Nó hoàn hảo để nhanh chóng hình dung commit hợp nhất và xem các nhánh đã được kết nối với nhau như thế nào.
- **Kiểm tra ứng dụng/mã nguồn của bạn:**
Kiểm tra cuối cùng là chạy mã của bạn! Thực hiện bất kỳ kiểm thử tự động nào, khởi động các quy trình xây dựng của bạn, hoặc thậm chí kiểm thử thủ công chức năng bị ảnh hưởng bởi các thay đổi đã hợp nhất. Bước quan trọng này xác minh rằng việc giải quyết xung đột của bạn không vô tình gây ra lỗi mới hoặc làm hỏng các tính năng hiện có. Đừng bỏ qua nó!
Đọc thêm & Tài nguyên
- [Phân nhánh Git - Phân nhánh và Hợp nhất cơ bản](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging)
- [Công cụ Git - Hợp nhất nâng cao](https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging)
- Tham khảo tài liệu của công cụ hợp nhất bạn đã chọn để biết hướng dẫn sử dụng cụ thể và các tính năng nâng cao.

