Bạn vừa chạy git pull để lấy code mới, nhưng terminal báo CONFLICT và repo đứng im? Đừng vội chạy reset --hard hay copy file từ nơi khác đè lên nhé. Đây là lúc cần xử lý chậm một chút để không mất code của mình hoặc của người trong team.
Trong bài này, mình sẽ hướng dẫn bạn xử lý git pull bị conflict theo quy trình an toàn: kiểm tra trạng thái, đọc conflict markers, sửa file, test lại, rồi hoàn tất merge hoặc rebase. Bài này thuộc series xử lý lỗi Git của VietnamTutor và là bài đi sâu từ pillar Lỗi Git thường gặp.

Tóm tắt nhanh
- Git pull bị conflict nghĩa là Git đã tải code mới nhưng không tự hợp nhất được một số thay đổi.
- Việc đầu tiên cần làm là chạy
git status, không chạy lệnh xóa thay đổi ngay. - Nếu pull theo merge, sau khi sửa file bạn dùng
git addrồigit commit. - Nếu pull theo rebase, sau khi sửa file bạn dùng
git addrồigit rebase --continue. - Nếu chưa chắc cách sửa, dùng
git merge --aborthoặcgit rebase --abortđể quay lại trạng thái trước đó.
Git pull bị conflict là gì?
Git pull bị conflict là tình huống Git đã fetch code từ remote, nhưng không thể tự động hợp nhất thay đổi đó vào branch hiện tại. Thường là vì cùng một dòng, cùng một file, hoặc cùng một phần logic đã bị sửa khác nhau ở local và remote.
Về bản chất, git pull không phải một lệnh đơn giản. Theo tài liệu Git, git pull sẽ lấy dữ liệu từ remote rồi tích hợp vào branch hiện tại bằng merge hoặc rebase, tùy cấu hình của bạn [1]. Vì vậy conflict sau khi pull có thể là conflict của merge hoặc conflict của rebase.
Bạn có thể thấy thông báo kiểu này:
git pull origin main # Git tải code mới từ remote Auto-merging app/config/payment.php CONFLICT (content): Merge conflict in app/config/payment.php Automatic merge failed; fix conflicts and then commit the result.
Thông báo này không có nghĩa là code đã mất. Git chỉ cần bạn quyết định phiên bản cuối cùng của file.

Vì sao chạy git pull lại sinh conflict?
Conflict thường xuất hiện khi local và remote cùng thay đổi một vùng code theo hai hướng khác nhau. Git có thể tự merge nhiều thay đổi độc lập, nhưng với thay đổi đè lên nhau thì Git dừng lại để bạn chọn.
Các tình huống hay gặp gồm:
- Bạn sửa cùng một dòng với đồng nghiệp trên branch
main. - Bạn sửa file cấu hình, remote cũng vừa đổi cấu hình đó.
- Một người đổi tên file, người khác sửa nội dung file cũ.
- File lock như
package-lock.jsonhoặcyarn.lockthay đổi ở nhiều branch. - Bạn để local thay đổi quá lâu rồi mới pull.
Ví dụ thực tế: bạn đang sửa phí vận chuyển trong file payment.php. Trong lúc đó, người khác thêm logic mã giảm giá vào đúng đoạn thanh toán. Khi bạn pull, Git không biết kết quả cuối nên ghép thế nào.
GitHub Docs cũng mô tả conflict như tình huống có thay đổi cạnh tranh giữa các branch; với conflict trên command line, bạn cần resolve local trước khi có thể push tiếp [2].
Cần kiểm tra gì trước khi sửa conflict?
Trước khi sửa file, bạn cần biết repo đang ở trạng thái merge hay rebase, file nào đang conflict, và local còn thay đổi nào chưa được xử lý. Lệnh quan trọng nhất lúc này là git status.
git status # Xem branch hiện tại, file conflict và hướng dẫn Git đề xuất
Tài liệu Git cho biết git status hiển thị trạng thái working tree, index, file staged, unstaged, untracked và cả các đường dẫn đang unmerged [3]. Khi đang conflict, output có thể như sau:
On branch feature/checkout
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: app/config/payment.phpBa dòng quan trọng cần đọc là:
fix conflicts and run "git commit": bạn đang ở flow merge.use "git merge --abort": có thể hủy merge nếu chưa muốn sửa.both modified: cả hai phía cùng sửa file.
Nếu output nói rebase in progress, flow sẽ khác một chút. Lúc đó bạn không commit merge trực tiếp, mà dùng git rebase --continue sau khi sửa file.

Cách xử lý git pull conflict từng bước
Quy trình an toàn là mở file conflict, xóa conflict markers sau khi đã chọn logic đúng, stage file, test lại, rồi hoàn tất merge hoặc rebase. Đừng chỉ xóa markers cho sạch nếu bạn chưa hiểu hai phía đang thay đổi gì.
Bước 1: Mở file bị conflict
Trong file, Git sẽ chèn markers giống như sau:
<<<<<<< HEAD
$shippingFee = 25000;
=======
$discountCode = $request->input('discount_code');
$shippingFee = 30000;
>>>>>>> origin/mainPhần trên HEAD là code ở branch hiện tại của bạn. Phần dưới origin/main là code từ remote vừa được pull về.
Bước 2: Quyết định kết quả cuối cùng
Ở ví dụ trên, chọn một phía là chưa đủ. Bạn cần giữ cả logic mã giảm giá và phí vận chuyển đúng:
$discountCode = $request->input('discount_code');
$shippingFee = 25000;Sau khi sửa xong, file không được còn các chuỗi <<<<<<<, =======, >>>>>>>. Mình khuyên bạn search nhanh trong repo để tránh commit sót markers.
grep -R "<<<<<<<" . # Nếu không có output, thường là không còn conflict marker
Bước 3: Stage file đã sửa
git add app/config/payment.php git status # Kiểm tra file đã chuyển khỏi nhóm unmerged paths chưa
Khi bạn chạy git add, bạn đang nói với Git rằng conflict của file đó đã được resolve. Nếu còn nhiều file conflict, xử lý từng file một. Đừng add toàn bộ bằng git add . khi bạn chưa xem hết file thay đổi.
Bước 4: Test lại trước khi commit
Đây là bước nhiều người bỏ qua. Với project web, tối thiểu bạn nên chạy test hoặc lệnh kiểm tra phù hợp:
# Ví dụ dự án Node.js npm test # Ví dụ dự án PHP/Laravel php artisan test
Nếu conflict nằm trong file lock như package-lock.json, cách an toàn thường là kiểm tra lại dependency, install lại, rồi chạy test. Với các workflow deploy như Docker hoặc Kubernetes, bạn cũng nên kiểm tra file cấu hình liên quan; có thể xem thêm bài Docker Compose best practices nếu conflict nằm trong cấu hình môi trường.
Bước 5: Hoàn tất merge
git commit # Git thường tự tạo message merge, bạn có thể giữ hoặc chỉnh cho rõ
Sau đó kiểm tra lại lịch sử:
git log --oneline --graph --decorate -5 git status
Nếu git status báo working tree clean, bạn đã xử lý xong conflict. Cùng xem một case thực tế để dễ hình dung hơn.

User case thực tế: hai người cùng sửa file cấu hình
Trong dự án thật, conflict hiếm khi chỉ là chọn dòng A hoặc dòng B; thường bạn phải ghép đúng ý định của cả hai thay đổi. Đây là ví dụ gần với tình huống team website hay gặp.
Giả sử bạn đang làm website bán hàng. Bạn phụ trách giảm phí ship cho đơn hàng nội thành. Đồng nghiệp phụ trách thêm mã giảm giá. Cả hai cùng sửa file checkout.php.
Code local của bạn:
// Tính phí ship nội thành $shippingFee = $customer->city === 'HCM' ? 15000 : 30000; $total = $subtotal + $shippingFee;
Code từ remote:
// Áp dụng mã giảm giá $discount = CouponService::calculate($request->coupon, $subtotal); $total = $subtotal - $discount + $shippingFee;
Nếu bạn chỉ chọn code của mình, tính năng mã giảm giá mất. Nếu chỉ chọn remote, phí ship nội thành mất. Kết quả đúng có thể là:
// Tính phí ship nội thành $shippingFee = $customer->city === 'HCM' ? 15000 : 30000; // Áp dụng mã giảm giá $discount = CouponService::calculate($request->coupon, $subtotal); // Tổng tiền cuối cùng $total = $subtotal - $discount + $shippingFee;
Đây là lý do mình không khuyên bạn dùng máy móc --ours hoặc --theirs cho conflict nghiệp vụ. Hai option đó có ích khi bạn chắc chắn muốn lấy một phía, nhưng với logic thanh toán, migration database, file cấu hình production hoặc file bảo mật, bạn cần đọc và test.
Bạn đang đọc bài viết thuộc chuyên mục Lập trình của VietnamTutor — nơi mình chia sẻ các workflow thực chiến để developer xử lý lỗi trong dự án thật, không chỉ học lệnh rời rạc.
Nếu git pull –rebase bị conflict thì làm sao?
Nếu conflict xảy ra khi pull rebase, bạn vẫn sửa file và chạy git add, nhưng bước cuối là git rebase --continue thay vì git commit. Đây là khác biệt dễ nhầm nhất.
Ví dụ:
git pull --rebase origin main Auto-merging app/config/payment.php CONFLICT (content): Merge conflict in app/config/payment.php error: could not apply 2a3b4c5... Update checkout fee
Flow xử lý:
# 1. Xem file đang conflict git status # 2. Sửa file trong editor # 3. Đánh dấu đã resolve git add app/config/payment.php # 4. Tiếp tục rebase git rebase --continue
Nếu commit tiếp theo lại conflict, Git sẽ dừng tiếp. Bạn lặp lại quy trình: sửa file, git add, rồi git rebase --continue.
Tài liệu Git cảnh báo rebase là chế độ có thể rewrite history, vì vậy không nên dùng bừa với history đã publish nếu bạn chưa hiểu tác động [1]. Với branch cá nhân trước khi tạo pull request, rebase thường ổn. Với branch chung nhiều người cùng push, hãy thống nhất convention trong team.

Khi nào nên abort thay vì cố sửa?
Bạn nên abort khi chưa hiểu conflict, khi file liên quan tới thanh toán/bảo mật/dữ liệu, hoặc khi local còn nhiều thay đổi chưa commit khiến trạng thái khó kiểm soát. Abort không phải thất bại; đó là cách quay lại điểm an toàn.
Nếu đang merge conflict:
git merge --abort
Nếu đang rebase conflict:
git rebase --abort
Tài liệu Git merge nói git merge --abort có thể dùng để dừng merge và cố gắng khôi phục trạng thái trước merge; tài liệu cũng cảnh báo merge khi có thay đổi local phức tạp có thể khiến việc quay lại khó hơn [4]. Vì vậy trước khi pull, nếu bạn đang sửa dở nhiều file, hãy commit tạm hoặc stash trước.
Ví dụ an toàn trước khi pull:
git status # Nếu đang sửa dở nhiều file, stash trước git stash push -m "WIP before pulling main" # Sau đó mới pull git pull origin main
Nếu bạn đang làm trong môi trường nhiều branch hoặc deploy phức tạp, nên ghi lại branch hiện tại trước khi sửa:
git branch --show-current git log --oneline -3
Hai dòng này giúp bạn biết mình đang đứng ở đâu nếu cần nhờ người khác review.
Cách tránh git pull conflict lần sau
Không thể tránh conflict 100%, nhưng bạn có thể giảm đáng kể bằng cách pull thường xuyên, chia commit nhỏ, tránh cùng sửa một file lớn và thống nhất workflow merge/rebase trong team. Conflict ít hơn nghĩa là review nhanh hơn, deploy đỡ rủi ro hơn.
Mình khuyên bạn áp dụng checklist này:
- Pull trước khi bắt đầu làm task mới.
- Commit nhỏ theo từng ý nghĩa, không gom quá nhiều thay đổi vào một commit.
- Không để branch cá nhân lệch quá lâu so với
main. - Trao đổi trước khi sửa file nhạy cảm như payment, auth, migration, config production.
- Với file lock, thống nhất cách regenerate thay vì sửa tay từng dòng.
- Trước khi merge pull request, chạy test và đọc lại diff.
Nếu bạn đang học Git theo lỗi thực tế, hãy lưu bài pillar Lỗi Git thường gặp. Series này sẽ đi từng lỗi một: conflict, commit nhầm, push rejected, detached HEAD, rebase lỗi và các tình huống recovery.

Nguồn tham khảo
- Git Documentation: git-pull
- GitHub Docs: Resolving a merge conflict using the command line
- Git Documentation: git-status
- Git Documentation: git-merge
- Git Documentation: git-diff
Các câu hỏi thường gặp
Git pull bị conflict có làm mất code không?
Thông thường là không. Git chỉ dừng lại vì không tự merge được. Code local và code remote vẫn nằm trong file conflict markers để bạn chọn hoặc ghép lại.
Sau khi sửa conflict xong cần chạy lệnh gì?
Nếu đang pull merge, bạn chạy git add với file đã sửa rồi git commit. Nếu đang pull rebase, bạn chạy git add rồi git rebase –continue.
Có nên dùng git merge –abort khi bị conflict không?
Có, nếu bạn chưa hiểu conflict hoặc cần quay lại trạng thái trước khi pull. Nhưng nếu trước đó working tree có nhiều thay đổi phức tạp, hãy đọc kỹ git status và backup trước.
Nên chọn ours hay theirs khi xử lý conflict?
Chỉ nên chọn ours hoặc theirs khi bạn chắc chắn muốn lấy toàn bộ một phía. Với logic nghiệp vụ như thanh toán, phân quyền, migration hoặc cấu hình production, nên đọc và ghép thủ công.
Git pull –rebase bị conflict khác gì pull thường?
Bước sửa file giống nhau, nhưng bước hoàn tất khác nhau. Pull thường dùng git commit sau khi git add. Pull rebase dùng git rebase –continue sau khi git add.
Làm sao biết còn conflict markers trong code?
Bạn có thể search các chuỗi <<<<<<<, ======= hoặc >>>>>>> trong repo. Nếu còn marker, không nên commit.
Tóm lại: Khi git pull bị conflict, việc đúng không phải là hoảng hay reset ngay. Hãy đọc git status, hiểu file nào đang conflict, sửa logic cuối cùng, test lại rồi mới commit hoặc rebase continue. Nếu chưa chắc, abort để quay lại điểm an toàn.
Bạn từng gặp conflict trong file nào khó xử lý nhất: file cấu hình, file lock hay logic nghiệp vụ? Để lại comment nhé, mình sẽ dùng các tình huống thực tế đó để viết tiếp các bài trong series Git.
