Triệu chứng chính: HEAD detached at ..., commit lên main thay vì feature branch, git reset --hard xong mất commit, ! [rejected] ... (fetch first) hoặc (non-fast-forward) khi push.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
HEAD detached at a1b2c3d |
Checkout một commit/tag thay vì branch | Giữ việc đang làm: git switch -c rescue; bỏ: git switch main |
Commit nhầm lên main (chưa push) |
Quên tạo branch | Xem mục "Commit nhầm branch" bên dưới |
Mất commit sau reset --hard / rebase |
HEAD đã dời, commit vẫn còn trong reflog | git reflog → git branch recover <sha> |
Stash bị drop nhầm |
Stash là commit không còn ref | git fsck --no-reflog | grep 'dangling commit' → git stash apply <sha> |
! [rejected] main -> main (fetch first) |
Remote có commit mới | git pull --rebase rồi push |
! [rejected] ... (non-fast-forward) |
Bạn vừa rebase/amend | Branch riêng: git push --force-with-lease; branch chung: không force, dùng git revert |
GH006: Protected branch update failed |
Branch được bảo vệ trên GitHub | Push lên branch khác và mở PR |
GH001: Large files detected |
File > 100 MB trong lịch sử | Bỏ file khỏi commit (git reset --soft) hoặc dùng Git LFS |
fatal: Not possible to fast-forward, aborting. |
pull.ff=only và hai bên đã rẽ nhánh |
git pull --rebase |
refusing to merge unrelated histories |
Hai repo không chung gốc (vd repo mới tạo có README) | git pull origin main --allow-unrelated-histories (chỉ khi chắc chắn) |
Your branch and 'origin/x' have diverged |
Cả local và remote có commit riêng | git log --oneline --left-right HEAD...origin/x rồi rebase hoặc merge |
| Lỡ commit secret | Secret nằm trong lịch sử, xoá file ở commit sau không đủ | Rotate secret trước, rồi xem git-secret-leak |
Các bước khoanh vùng
1. Biết mình đang ở đâu
git status
git branch -vv # branch hiện tại, upstream, ahead/behind
git log --oneline --graph -15 --all2. Commit nhầm branch (chưa push)
# Đang ở main, có 2 commit lẽ ra thuộc feature
git branch feature/fix-acl # giữ 2 commit ở branch mới
git reset --hard origin/main # đưa main về như remote
git switch feature/fix-aclNếu feature branch đã tồn tại sẵn thì cherry-pick sang:
git switch feature/fix-acl
git cherry-pick <sha1> <sha2>
git switch main && git reset --hard origin/mainNếu đã push lên main thì không reset, dùng git revert <sha> để tạo commit đảo ngược.
3. Cứu commit bằng reflog
git reflog -20
# e4f5a6b HEAD@{0}: reset: moving to HEAD~3
# 9c8d7e6 HEAD@{1}: commit: add prefix-list
git branch recover 9c8d7e6 # an toàn: tạo branch trỏ vào commit cũ
# hoặc đưa branch hiện tại về đó
git reset --hard HEAD@{1}Reflog chỉ có trên máy local và mặc định giữ khoảng 90 ngày (commit không còn ref giữ khoảng 30 ngày) trước khi git gc dọn.
4. Undo đúng mức
| Tình huống | Lệnh |
|---|---|
| Sửa message commit cuối (chưa push) | git commit --amend |
| Bỏ commit cuối, giữ code | git reset --soft HEAD~1 |
| Commit đã push lên branch chung | git revert <sha> |
| Huỷ merge đang dở | git merge --abort |
| Huỷ rebase đang dở | git rebase --abort |
| Lấy lại 1 file từ commit trước | git restore --source=HEAD~1 -- path/file |
Kiểm tra nhanh
git status -sb
git log --oneline --left-right --graph HEAD...@{upstream} # < local, > remote
git fetch --prune && git branch -vv | grep ': gone]' # branch có remote đã bị xoá
git reflog -10
git fsck --no-reflog | grep 'dangling commit'Trước thao tác nguy hiểm, tạo branch backup: git branch backup/$(date +%F-%H%M). Tốn 0 byte, cứu được cả buổi chiều.