Bạn vừa chạy git reset --hard, rebase nhầm hoặc xóa một branch chứa commit chưa push? Cảm giác đầu tiên thường là hoảng: mở git log không thấy commit đâu nữa, IDE cũng không giúp được nhiều. Đừng vội chạy thêm lệnh mạnh nhé.
Git reflog thường là nơi đầu tiên bạn nên kiểm tra khi cần khôi phục commit đã mất. Trong bài này, mình sẽ hướng dẫn bạn đọc reflog, tìm đúng commit, tạo nhánh recovery an toàn, rồi quyết định nên merge, cherry-pick hay reset. Bài này tiếp nối series xử lý lỗi Git của VietnamTutor, đi sâu từ bài pillar Cách xử lý lỗi Git thường gặp.

Tóm tắt nhanh
- Git reflog ghi lại lịch sử di chuyển của HEAD và các ref trong repo local, kể cả khi commit không còn hiện trong
git log. - Cách an toàn nhất là xem reflog, kiểm tra commit bằng
git show, rồi tạo branch mới từ hash tìm được. - Reflog giúp trong các tình huống reset nhầm, rebase lỗi, amend sai, detached HEAD hoặc xóa branch local.
- Reflog không phải backup vĩnh viễn: entry reachable thường hết hạn sau 90 ngày, còn entry unreachable mặc định có thể hết hạn sớm hơn.
- Trước khi dùng
reset --hard, hãy tạo branch recovery để có đường lui.
Git reflog là gì?
Git reflog là nhật ký local ghi lại các lần HEAD, branch tip và một số reference khác thay đổi vị trí. Nói dễ hiểu, git log cho bạn xem lịch sử commit đang còn nối với branch, còn reflog cho bạn xem “HEAD đã từng đứng ở đâu”.
Theo tài liệu chính thức của Git, reflog ghi lại thời điểm tip của branch và các reference được cập nhật trong repository local; ví dụ HEAD@{2} nghĩa là nơi HEAD từng trỏ tới hai lần di chuyển trước đó [1]. Đây là khác biệt rất quan trọng: commit có thể biến mất khỏi lịch sử branch hiện tại, nhưng vẫn còn xuất hiện trong reflog một thời gian.
- Reflog
- Nhật ký local ghi lại các lần ref thay đổi, dùng để tìm lại commit, branch tip hoặc trạng thái trước reset, rebase, amend và checkout.
Ví dụ bạn reset branch về 3 commit trước. git log lúc này không còn thấy 3 commit mới, nhưng git reflog vẫn có thể ghi lại điểm trước khi reset:
git reflog
# Ví dụ output:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# e4f5g6h HEAD@{1}: commit: thêm luồng thanh toán MoMo
# 7a8b9c0 HEAD@{2}: commit: sửa validate đơn hàngĐiểm cần nhớ: reflog là dữ liệu local. Nếu bạn clone lại repo mới từ GitHub hoặc GitLab, bạn không có reflog cũ của máy trước đó. Vì vậy, khi phát hiện mất commit, hãy dừng thao tác trong đúng repo đang gặp sự cố.

Khi nào cần dùng reflog?
Bạn nên dùng reflog khi commit hoặc branch vừa biến mất sau một thao tác thay đổi lịch sử local. Các tình huống phổ biến nhất là reset nhầm, rebase lỗi, amend sai commit, checkout detached HEAD hoặc xóa branch local còn commit chưa merge.
Đây là các case rất thường gặp trong dự án thật:
- Bạn chạy
git reset --hard HEAD~3và mất 3 commit mới nhất. - Bạn rebase interactive rồi vô tình drop một commit quan trọng.
- Bạn dùng
git commit --amendvà muốn lấy lại commit trước khi amend. - Bạn xóa branch local bằng
git branch -D feature/payment. - Bạn commit trong trạng thái detached HEAD rồi checkout sang branch khác.
- Bạn pull hoặc rebase bị lỗi và muốn tìm lại trạng thái trước đó.
Nếu vấn đề của bạn là git pull bị conflict, hãy xử lý conflict trước theo đúng trạng thái merge hoặc rebase. Mình đã tách hướng dẫn riêng tại bài Git pull bị conflict. Reflog hữu ích hơn khi bạn đã lỡ làm commit không còn nằm trên branch hiện tại.
Một nguyên tắc thực dụng: nếu commit đã từng được tạo trong repo local, khả năng cao bạn còn cơ hội tìm lại. Nếu thay đổi chưa từng commit, reflog không cứu được nội dung đó vì Git chưa có object commit để tham chiếu.
Cách đọc output reflog
Mỗi dòng reflog gồm hash commit, chỉ số như HEAD@{1}, loại thao tác và message giúp bạn đoán điểm cần khôi phục. Bạn không cần hiểu hết nội bộ Git ngay; chỉ cần biết dòng nào là điểm trước khi sự cố xảy ra.
git reflog --date=iso
# Ví dụ:
# a1b2c3d HEAD@{2026-06-07 09:12:10 +0700}: reset: moving to HEAD~3
# e4f5g6h HEAD@{2026-06-07 09:10:02 +0700}: commit: thêm luồng thanh toán MoMo
# 7a8b9c0 HEAD@{2026-06-07 08:55:41 +0700}: checkout: moving from feature/payment to mainTrong output trên:
a1b2c3dlà vị trí hiện tại sau reset.HEAD@{0}hoặc timestamp là cách Git gọi từng mốc trong reflog.reset: moving to HEAD~3cho biết thao tác gây thay đổi.- Dòng ngay trước reset thường là ứng viên cần kiểm tra.
Trước khi recovery, hãy kiểm tra commit bằng git show hoặc git log -1. Tài liệu git reset cũng nêu rằng reset có thể thay đổi trạng thái HEAD, index và working tree tùy chế độ, nên việc kiểm tra trước khi reset ngược lại là bắt buộc [2].
git show e4f5g6h # Xem message, file thay đổi và diff trước khi khôi phục
Thú vị nhỉ: nhiều lúc commit “mất” thực ra vẫn còn nguyên, chỉ là branch pointer không trỏ tới nó nữa.

Khôi phục commit sau reset hard
Cách an toàn nhất sau khi reset hard nhầm là tìm commit trong reflog, tạo branch recovery từ hash đó, rồi kiểm tra nội dung trước khi đưa lại vào branch chính. Đừng reset tiếp ngay nếu bạn chưa chắc hash nào là đúng.
Giả sử bạn vừa làm thế này:
git reset --hard HEAD~3 # Lỡ đưa branch lùi 3 commit và working tree cũng bị đưa về trạng thái cũ
Quy trình recovery nên đi như sau:
Bước 1: Xem reflog
git reflog --date=iso
Bước 2: Tìm commit ngay trước thao tác reset
# Ví dụ thấy dòng:
# e4f5g6h HEAD@{1}: commit: thêm luồng thanh toán MoMoBước 3: Tạo branch recovery
git branch recovery/payment e4f5g6h git switch recovery/payment git log --oneline --decorate -5
Mình khuyên bạn tạo branch trước vì nó ít rủi ro hơn reset branch hiện tại. Khi commit đã có branch trỏ tới, bạn có thể review, chạy test, rồi quyết định merge hoặc cherry-pick.
Bước 4: Đưa commit về branch đang làm
Nếu muốn giữ toàn bộ nhánh recovery:
git switch feature/payment git merge recovery/payment
Nếu chỉ muốn lấy một commit cụ thể, dùng git cherry-pick. Tài liệu Git mô tả cherry-pick là thao tác áp dụng thay đổi từ commit đã có vào HEAD hiện tại, phù hợp khi bạn chỉ cần một hoặc vài commit [3].
git switch feature/payment git cherry-pick e4f5g6h
Chỉ dùng git reset --hard e4f5g6h khi bạn hiểu rõ branch hiện tại sẽ bị đưa thẳng về commit đó và thay đổi working tree có thể bị mất. Với team workflow, tạo branch recovery trước vẫn là lựa chọn tỉnh táo hơn.

Khôi phục branch đã xóa bằng reflog
Nếu branch local bị xóa nhưng commit của branch từng được checkout hoặc commit trên máy bạn, reflog thường giúp tìm lại tip cuối cùng của branch đó. Sau khi tìm được hash, bạn chỉ cần tạo lại branch từ hash này.
Ví dụ bạn xóa nhầm:
git branch -D feature/checkout-flow
Hãy tìm các dòng liên quan đến branch:
git reflog --all --date=iso | grep "feature/checkout-flow" # Nếu shell không có grep, bạn có thể chạy git reflog --all rồi search trong terminal
Khi thấy commit cuối:
git branch feature/checkout-flow 9abc123 git switch feature/checkout-flow git log --oneline --decorate -5
Nếu không nhớ tên branch, hãy đọc reflog theo thời gian gần nhất. Những dòng checkout: moving from..., commit:, rebase: thường là manh mối tốt. Bài hướng dẫn recovery của CoreUI cập nhật tháng 1/2026 cũng khuyến nghị kiểm tra commit bằng git show và tạo branch mới thay vì phục hồi mù quáng [4].
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 reflog không thấy commit thì làm gì?
Nếu reflog không còn entry cần tìm, bạn vẫn có thể thử git fsck để tìm dangling commit, nhưng khả năng cứu sẽ thấp hơn và cần kiểm tra kỹ hơn. Đây là phương án sau reflog, không phải bước đầu tiên.
Git có cơ chế dọn dẹp repository. Tài liệu reflog ghi rõ entry reachable mặc định lấy từ gc.reflogExpire là 90 ngày, còn entry unreachable mặc định lấy từ gc.reflogExpireUnreachable là 30 ngày [1]. Tài liệu git gc cũng cho biết garbage collection có thể prune reflog và object không còn cần thiết trong quá trình housekeeping [5].
Nếu reflog không thấy commit, thử:
git fsck --lost-found # Tìm các dangling commit còn sót trong object database
Tài liệu git fsck mô tả lệnh này dùng để kiểm tra tính kết nối và hợp lệ của object trong database Git [6]. Khi thấy dangling commit, bạn cần xem từng commit:
git show abc123456789 git log -1 --oneline abc123456789 # Nếu đúng commit cần cứu git branch recovery/from-fsck abc123456789
Lưu ý quan trọng: nếu bạn chưa từng commit thay đổi, cả reflog lẫn fsck đều khó giúp. Lúc đó hy vọng nằm ở local history của IDE, backup hệ thống, Time Machine hoặc bản build artifact nếu có.
Checklist an toàn trước khi recovery
Recovery bằng reflog nên đi theo nguyên tắc dừng lại, quan sát, tạo điểm an toàn, rồi mới thay đổi branch chính. Càng hoảng càng dễ chạy thêm lệnh làm mất dấu vết cần cứu.
Checklist mình khuyên bạn dùng:
- Dừng chạy
reset --hard,clean -fd,gc --prune=nowhoặc force push nếu chưa hiểu trạng thái. - Chạy
git statusđể biết repo đang clean, merge, rebase hay có thay đổi chưa commit. - Chạy reflog với tùy chọn
--date=isođể dễ đọc theo thời gian. - Dùng
git show <hash>trước khi khôi phục. - Tạo branch recovery bằng
git branch recovery/name <hash>. - Chạy test hoặc ít nhất review diff trước khi merge/cherry-pick.
- Nếu sự cố liên quan branch đã push chung, trao đổi với team trước khi rewrite history.
Bảng dưới đây giúp bạn chọn lệnh theo tình huống:
| Tình huống | Lệnh nên dùng trước | Cách phục hồi an toàn |
|---|---|---|
| Reset hard nhầm | git reflog --date=iso | Tạo branch từ commit trước reset |
| Xóa branch local | git reflog --all | Tạo lại branch từ commit cuối |
| Rebase làm mất commit | Xem reflog | Tạo branch hoặc cherry-pick commit bị drop |
| Amend sai commit | Xem reflog | So sánh commit trước/sau amend rồi reset soft hoặc branch recovery |
| Reflog không còn entry | git fsck --lost-found | Kiểm tra dangling commit rồi tạo branch |
Nếu bạn đang xây workflow Git cho team, hãy biến checklist này thành thói quen trước các thao tác rủi ro. Với các dự án web có CI/CD, Docker hoặc nhiều người cùng sửa file cấu hình, recovery chỉ là lớp cứu hộ cuối; cách tốt hơn vẫn là commit nhỏ, pull thường xuyên và review kỹ trước khi chạy lệnh thay đổi history.

Nguồn tham khảo
- Git documentation: git-reflog
- Git documentation: git-reset
- Git documentation: git-cherry-pick
- CoreUI: How to recover deleted commit in Git
- Git documentation: git-gc
- Git documentation: git-fsck
- Uptut: Git Reflog and Recovery Playbook
Các câu hỏi thường gặp
Git reflog khác gì git log?
git log hiển thị lịch sử commit reachable từ branch hiện tại, còn git reflog hiển thị lịch sử di chuyển của HEAD và ref trong repo local. Vì vậy reflog có thể thấy commit vừa bị reset hoặc rebase làm rơi khỏi branch.
Git reflog có khôi phục được file chưa commit không?
Không chắc. Reflog chủ yếu giúp tìm lại commit hoặc ref đã từng tồn tại. Nếu thay đổi chưa từng được commit, bạn nên kiểm tra local history của IDE, backup hệ thống hoặc stash nếu trước đó đã dùng git stash.
Có nên dùng git reset –hard để khôi phục commit từ reflog không?
Chỉ nên dùng khi bạn hiểu rõ hậu quả. Cách an toàn hơn là tạo branch recovery từ hash tìm được, kiểm tra nội dung, rồi merge hoặc cherry-pick vào branch chính. Reset hard trực tiếp có thể làm mất thay đổi working tree hiện tại.
Git reflog lưu được bao lâu?
Mặc định phổ biến là 90 ngày cho reflog entry reachable và 30 ngày cho entry unreachable, theo cấu hình gc.reflogExpire và gc.reflogExpireUnreachable. Tuy nhiên repo có thể được cấu hình khác, nên đừng chờ quá lâu mới recovery.
Git reflog có được push lên GitHub không?
Không. Reflog là nhật ký local của từng repository trên máy bạn. Khi clone repo mới, bạn không có reflog cũ từ máy khác. Nếu cần bảo vệ commit quan trọng, hãy push branch, tạo tag hoặc backup trước thao tác rủi ro.
Sau khi tìm thấy commit bằng reflog, nên merge hay cherry-pick?
Nếu bạn muốn lấy toàn bộ nhánh recovery, dùng merge. Nếu chỉ cần một hoặc vài commit cụ thể, dùng cherry-pick. Trước cả hai lựa chọn, hãy chạy git show và review diff để chắc chắn commit đúng.
Tóm lại, git reflog là kỹ năng nên biết trước khi bạn thật sự cần đến nó. Khi commit biến mất, hãy bình tĩnh: kiểm tra trạng thái, đọc reflog, tạo branch recovery và xác minh nội dung trước khi đưa code trở lại. Nếu bài này giúp bạn cứu được một commit quan trọng, hãy lưu lại để lần sau xử lý nhanh hơn nhé!
