
Bạn đang code như thường, rồi bỗng dưng terminal báo lỗi. Merge conflict xuất hiện. Push không được. Detached HEAD làm mất commit. Những lỗi này không hiếm gặp — theo khảo sát của Stack Overflow năm 2025, Git là công cụ version control được dùng nhiều nhất với 97% developer sử dụng [1]. Kể cả senior developer cũng gặp những lỗi này hàng tuần.
Bài viết này hướng dẫn chi tiết cách xử lý từng lỗi Git phổ biến nhất, kèm lệnh cụ thể để bạn copy-paste là xong.
Tóm tắt nhanh
- Merge conflict — Xung đột code khi merge 2 nhánh. Xử lý bằng cách chỉnh sửa file, bỏ conflict markers, rồi
git add. - Detached HEAD — Checkout nhầm commit cũ. Tạo nhánh mới từ commit hiện tại hoặc quay lại nhánh cũ.
- Push thất bại — Thường do remote có commit mới. Dùng
git pull --rebasehoặcgit pulltrước khi push. - Permission denied (publickey) — SSH key chưa được thêm vào agent. Kiểm tra bằng
ssh -T git@github.com. - Refusing unrelated histories — Merge 2 repo riêng biệt. Dùng cờ
--allow-unrelated-histories. - Recover lost commits — Dùng
git reflogđể tìm lại commit đã mất.
Cách xử lý lỗi Merge Conflict trong Git
Merge conflict xảy ra khi Git không thể tự động hợp nhất thay đổi từ 2 nhánh vì cùng một dòng code được sửa khác nhau. Đây là lỗi phổ biến nhất khi làm việc nhóm, đặc biệt khi nhiều developer cùng sửa một file [2]. Theo GitHub Docs, conflict xảy ra khi hai người thay đổi cùng một dòng hoặc một người xóa file trong khi người khác đang sửa file đó [3].
Khi merge thất bại, Git sẽ báo như sau:
git merge feature-branch Auto-merging src/config.js CONFLICT (content): Merge conflict in src/config.js Automatic merge failed; fix conflicts and then commit the result.
Bước 1: Xem file nào bị conflict
git status # Output: # On branch main # You have unmerged paths. # (fix conflicts and run "git commit") # Unmerged paths: # both modified: src/config.js
Bước 2: Mở file và xử lý conflict markers
Git đánh dấu vùng conflict bằng ba markers:
<<<<<<< HEAD Dòng code của nhánh hiện tại (main) ======= Dòng code của nhánh bạn đang merge (feature-branch) >>>>>>> feature-branch
Bạn chỉ cần chỉnh sửa file, xóa ba markers và giữ lại phần code đúng:
const config = {
appName: 'MyApp',
timeout: 5000, // Chọn giá trị đúng
debug: false,
};Bước 3: Stage và commit
# Stage file đã xử lý git add src/config.js # Hoàn tất merge git commit -m "Merge feature-branch: resolve conflict in config.js"
Dùng Merge Tool cho nhanh
Nếu file phức tạp, dùng mergetool để so sánh trực quan:
# Liệt kê các tool có sẵn git mergetool --tool-help # Mở merge tool git mergetool
VS Code, P4Merge, Beyond Compare là những công cụ phổ biến để resolve conflict trực quan [4].
Hủy merge khi cần
Nếu conflict quá phức tạp, hủy bỏ hoàn toàn:
git merge --abort
Lệnh này an toàn — không mất bất kỳ commit nào, chỉ đưa repo về trạng thái trước khi merge [5].

Detached HEAD — Khi nhỡ checkout nhầm commit cũ
Detached HEAD xảy ra khi bạn checkout một commit cụ thể thay vì một nhánh. HEAD lúc này không trỏ vào nhánh, mà trỏ thẳng vào commit. Mọi thay đổi bạn tạo ra sẽ không được lưu vào nhánh nào cả — nếu không tạo nhánh mới, chúng sẽ bị mất khi checkout sang nhánh khác [6].
Bạn sẽ thấy thông báo này:
git checkout abc1234 Note: switching to 'abc1234'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them. If you want to keep the changes, create a branch.
Cách 1: Tạo nhánh mới để giữ thay đổi
# Tạo nhánh mới từ commit hiện tại git checkout -b fix-from-detached
Cách 2: Quay lại nhánh cũ nếu chưa có thay đổi
# Quay về nhánh main git checkout main
Cách 3: Nếu đã checkout đi rồi — dùng reflog
# Xem lịch sử HEAD di chuyển
git reflog
# Output:
# abc1234 HEAD@{0}: checkout: moving from main to abc1234
# def5678 HEAD@{1}: commit: add user profile feature
# ...
# Quay về commit trước khi detach
git checkout -b recovery def5678
Push thất bại: “Failed to Push Some Refs”
Lỗi “failed to push some refs” thường xảy ra khi remote có commit mới mà local chưa có. Git từ chối ghi đè để tránh mất dữ liệu của người khác [7].
Dấu hiệu nhận biết
git push origin main ! [rejected] main -> main (fetch first) error: failed to push some refs hint: Updates were rejected because the remote contains work that you do not hint: have locally. This is usually caused by another repository pushing hint: to the same ref.
Giải pháp 1: Pull rồi push (merge)
git pull origin main # Resolve conflict nếu có git push origin main
Giải pháp 2: Rebase để lịch sử gọn hơn
git pull --rebase origin main # Resolve conflict nếu có git push origin main
Rebase đặt commit local của bạn lên trên commit remote, giữ lịch sử linear và sạch hơn [8].
Giải pháp 3: Force push (dùng cẩn thận)
# Chỉ dùng khi bạn chắc chắn không mất commit git push --force origin main
Cảnh báo: Force push sẽ ghi đè hoàn toàn remote history. Không bao giờ dùng trên nhánh chính (main/master). Chỉ dùng trên nhánh feature cá nhân khi đã chắc chắn.

Permission Denied (publickey) — Lỗi SSH Key
Lỗi “Permission denied (publickey)” xảy ra khi SSH key của bạn chưa được thêm vào Git server hoặc bị sai cấu hình. Đây là lỗi phổ biến khi thiết lập GitHub, GitLab, Bitbucket lần đầu [9].
Kiểm tra SSH key
# Kiểm tra SSH key có tồn tại không ls -la ~/.ssh/ # Kiểm tra key đã được thêm vào SSH agent chưa ssh-add -l # Nếu chưa có key nào ssh-add ~/.ssh/id_ed25519 # Hoặc ssh-add ~/.ssh/id_rsa
Kết nối thử với GitHub
ssh -T git@github.com # Nếu thành công, bạn sẽ thấy: # Hi username! You've successfully authenticated.
Nếu dùng HTTPS thay vì SSH
Nếu không muốn cấu hình SSH, đổi remote sang HTTPS:
# Xem remote hiện tại git remote -v # Đổi từ SSH sang HTTPS git remote set-url origin https://github.com/username/repo.git # Hoặc dùng token thay vì password # GitHub không còn hỗ trợ password từ 2021 git remote set-url origin https://username:TOKEN@github.com/username/repo.git

Refusing to Merge Unrelated Histories
Lỗi này xảy ra khi bạn cố merge hai repository hoàn toàn riêng biệt, không có lịch sử commit chung. Git mặc định từ chối vì rủi ro mất dữ liệu [10].
git merge another-repo fatal: refusing to merge unrelated histories
Giải pháp: Dùng cờ –allow-unrelated-histories
git merge another-repo --allow-unrelated-histories # Sau đó resolve conflict nếu có # Và commit git commit -m "Merge unrelated repository"
Cờ này bảo Git hợp nhất hai lịch sử commit riêng biệt. Thường dùng khi import repository cũ hoặc ghép hai dự án lại.
Khôi phục Commit đã Mất với Git Reflog
Git Reflog lưu lại toàn bộ lịch sử di chuyển của HEAD, kể cả các commit đã bị xóa hoặc reset. Đây là “bảo hiểm” cuối cùng khi mọi thứ khác thất bại [6].
Tìm commit đã mất
git reflog
# Output mẫu:
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: add payment feature
# 9876543 HEAD@{2}: commit: fix navigation bug
# 1111111 HEAD@{3}: commit: initial setupKhôi phục commit
# Cách 1: Tạo nhánh mới từ commit đã mất git checkout -b recovery def5678 # Cách 2: Reset HEAD về commit đã mất git reset --hard def5678
Reflog giữ lại lịch sử trong 90 ngày mặc định, nên bạn có thời gian để phát hiện và khôi phục [6].

Cách Undo Thay Đổi trong Git
Git cung cấp nhiều cách undo khác nhau, từ unstage file đến revert commit đã push. Chọn đúng lệnh tùy tình huống cụ thể.
Undo thay đổi chưa staged
# Undo thay đổi trên file cụ thể git checkout -- src/app.js # Hoặc dùng (Git 2.23+) git restore src/app.js
Unstage file đã add
# Unstage một file git reset HEAD src/app.js # Unstage tất cả files git reset HEAD
Undo commit chưa push
# Giữ thay đổi ở working directory (soft reset) git reset --soft HEAD~1 # Xóa commit nhưng giữ thay đổi ở staging (mixed reset - mặc định) git reset HEAD~1 # Xóa hoàn toàn commit và thay đổi (hard reset) git reset --hard HEAD~1
Revert commit đã push (an toàn nhất)
# Tạo commit mới để undo commit cũ (không xóa lịch sử) git revert abc1234 # Push commit revert git push origin main
Revert là cách an toàn nhất để undo commit đã push vì nó tạo commit mới thay vì xóa commit cũ, không làm ảnh hưởng lịch sử repo [5].
Cách phòng ngừa Conflict hiệu quả
Phòng ngừa luôn tốt hơn xử lý. Dưới đây là những thực hành giúp giảm conflict đáng kể [2]:
1. Pull trước khi code
# Luôn cập nhật nhánh chính trước khi bắt đầu git checkout main git pull origin main git checkout -b feature/your-feature
2. Giữ nhánh ngắn gọn
Tạo nhánh, code xong, tạo Pull Request nhanh — không nên giữ nhánh mở hàng tuần. Càng ít thời gian, càng ít conflict.
3. Giao tiếp với team
Nếu bạn và đồng nghiệp cùng sửa một file, hãy thông báo trước. Một tin nhắn trong Slack có thể tiết kiệm hàng giờ debug.
4. Dùng .gitattributes
Khai báo cách xử lý file nhị phân hoặc merge specific files:
# .gitattributes *.json merge=union *.xml merge=union
5. Bật git rerere
# Git tự động ghi nhớ cách bạn resolve conflict trước đó git config --global rerere.enabled true
rerere (reuse recorded resolution) tự động apply cách resolve conflict đã ghi nhớ cho các conflict tương tự trong tương lai [4].

Nguồn tham khảo
- Stack Overflow Developer Survey 2025
- How to Fix Merge Conflict Errors in Git – OneUptime (2026)
- About Merge Conflicts – GitHub Docs (2025)
- Git Merge Conflicts: The Complete Guide – DevToolbox Blog (2026)
- Resolve Merge Conflicts Effectively – Mergify (2025)
- Git Troubleshooting: Merge Conflicts, Detached HEAD, Recover Lost Commits – DEV Community (2024)
- Merge Conflict In Git: Resolving Merge Conflicts (2025 Edition) – PWSkills
- Common Git Errors and How to Fix Them – Metana (2024)
Các câu hỏi thường gặp
Merge conflict là gì và tại sao nó xảy ra?
Merge conflict xảy ra khi Git không thể tự động hợp nhất thay đổi từ 2 nhánh. Nguyên nhân phổ biến: hai developer sửa cùng một dòng code, một nhánh xóa file trong khi nhánh kia sửa file đó, hoặc hai nhánh cùng thêm code vào cùng một vị trí. Git sẽ dừng merge và yêu cầu bạn xử lý thủ công.
Làm sao để hủy bỏ một merge đang bị conflict?
Dùng lệnh git merge --abort để hủy bỏ hoàn toàn. Lệnh này an toàn, không mất commit nào, chỉ đưa repo về trạng thái trước khi bắt đầu merge. Với rebase bị conflict, dùng git rebase --abort.
Detached HEAD có nguy hiểm không?
Detached HEAD không nguy hiểm cho repo chính. Tuy nhiên, mọi commit bạn tạo ra trong trạng thái này sẽ bị mất nếu checkout sang nhánh khác mà không tạo nhánh mới. Luôn tạo nhánh mới (git checkout -b) nếu muốn giữ thay đổi.
Khi nào nên dùng git revert thay vì git reset?
Dùng git revert khi commit đã được push lên remote. Revert tạo commit mới undo commit cũ, không xóa lịch sử — an toàn cho team. Dùng git reset chỉ khi commit chưa push và bạn muốn loại bỏ hoàn toàn commit đó khỏi lịch sử.
Làm sao khôi phục commit đã xóa do reset –hard?
Dùng git reflog để xem lịch sử di chuyển của HEAD. Tìm commit đã mất (sẽ vẫn còn trong reflog 90 ngày), sau đó tạo nhánh mới từ commit đó: git checkout -b recovery abc1234.
Tại sao git push bị rejected và cách xử lý?
Push bị rejected vì remote có commit mới mà local chưa có. Git từ chối để tránh ghi đè commit của người khác. Cách xử lý: pull remote về trước (git pull --rebase origin main), resolve conflict nếu có, rồi push lại.
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ẻ những kiến thức thực chiến về công cụ và quy trình phát triển phần mềm, từ Git cơ bản đến DevOps nâng cao.
