Cách Xử Lý Lỗi Git Thường Gặp: Hướng Dẫn Chi Tiết 2026

Nội dung

Git là công cụ quản lý phiên bản phổ biến, nhưng lỗi conflict, detached HEAD hay push thất bại có thể khiến developer mất hàng giờ. Bài viết này hướng dẫn chi tiết cách xử lý từng lỗi phổ biến nhất.
Hướng dẫn xử lý lỗi Git thường gặp 2026 - merge conflict, detached HEAD, push thất bại
Hướng dẫn xử lý lỗi Git thường gặp: từ merge conflict đến khôi phục commit đã mất

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 --rebase hoặc git pull trướ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].

Sơ đồ merge conflict trong Git khi hai nhánh thay đổi cùng một dòng
Merge conflict xảy ra khi Git không tự hợp nhất được thay đổi

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

Sơ đồ trạng thái Detached HEAD trong Git so với trạng thái bình thường
Detached HEAD — HEAD trỏ thẳng vào commit thay vì nhánh

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.

Sơ đồ git push bị rejected do remote có commit mới
Push bị rejected khi remote đã có commit mới từ người khác

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

Sơ đồ xác thực SSH key với GitHub và các bước kiểm tra
Quy trình xác thực SSH key với GitHub — kiểm tra và khắc phục lỗi permission denied

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 setup

Khô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].

Sơ đồ git reflog hoạt động như bảo hiểm khôi phục commit đã mất
Git reflog ghi lại mọi di chuyển của HEAD — dùng để khôi phục commit đã mất

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].

Hình ảnh minh họa các thực hành tốt khi sử dụng Git để tránh conflict
Thực hành tốt khi dùng Git: pull thường xuyên, nhánh ngắn gọn, và giao tiếp team

Nguồn tham khảo

  1. Stack Overflow Developer Survey 2025
  2. How to Fix Merge Conflict Errors in Git – OneUptime (2026)
  3. About Merge Conflicts – GitHub Docs (2025)
  4. Git Merge Conflicts: The Complete Guide – DevToolbox Blog (2026)
  5. Resolve Merge Conflicts Effectively – Mergify (2025)
  6. Git Troubleshooting: Merge Conflicts, Detached HEAD, Recover Lost Commits – DEV Community (2024)
  7. Merge Conflict In Git: Resolving Merge Conflicts (2025 Edition) – PWSkills
  8. 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.

Tú Anh

Cây bút chính tại VietnamTutor

Bài viết cùng chuyên mục

Cách Audit TTFB Cho WordPress: Giảm Server Response Time Hiệu Quả

Hướng dẫn chi tiết cách audit TTFB cho WordPress từ server hardware đến plugin nhẹ. Giảm server response time từ 2s xuống dưới 200ms.

WooCommerce 10.6.1: Hướng Dẫn Tối Ưu Performance Toàn Diện

WooCommerce 10.6.1 sửa lỗi attribute validation, payment gateway ordering. Hướng dẫn tối ưu performance toàn diện cho shop bán hàng.

WordPress Backup Restore: Checklist 6 Bước + Restore Drill

WordPress backup restore thật sự dùng được khi cần. Checklist 6 bước và hướng dẫn restore drill định kỳ.

Structured Data Report Trong Search Console: Hướng Dẫn Mới 2026

Google đã thay đổi structured data report. Hướng dẫn theo dõi và debug structured data mới nhất 2026.

Search Console Query Groups: Tìm Content Gap và Refresh Bài

Hướng dẫn sử dụng Search Console Query Groups để gom truy vấn theo intent, phát hiện content gap và chọn bài cần refresh trong chiến lược

WordPress staging checklist 2026: Test update an toàn trước khi đẩy live

Hướng dẫn chủ website lập staging checklist đủ dùng: backup, clone, test form, test checkout, test tracking, rollback.