Git reflog: khôi phục commit đã mất an toàn

Nội dung

Bài viết hướng dẫn dùng git reflog để khôi phục commit đã mất sau reset, rebase, amend hoặc xóa nhánh. Bạn sẽ biết cách đọc reflog, tạo branch recovery và kiểm tra commit trước khi đưa lại vào code chính.

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.

Git reflog giúp khôi phục commit đã mất an toàn
Reflog là dòng thời gian cứu hộ khi commit tưởng như đã biến mất.

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

Sơ đồ so sánh git reflog và git log trong khôi phục commit
Git log và reflog trả lời hai câu hỏi khác nhau khi bạn cần recovery.

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~3 và 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 --amend và 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 main

Trong output trên:

  • a1b2c3d là 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~3 cho 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.

Minh họa cách đọc output git reflog với HEAD và commit hash
Đọc đúng từng dòng reflog giúp bạn chọn đúng commit cần cứu.

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 MoMo

Bướ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.

Quy trình dùng git reflog khôi phục commit sau reset hard
Sau reset hard, tạo branch recovery trước giúp giảm rủi ro thao tác sai lần hai.

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.

Khôi phục branch đã xóa bằng git reflog và tạo nhánh mới
Khi branch pointer mất, commit cuối vẫn có thể được gắn lại bằng một branch mới.

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=now hoặ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ốngLệnh nên dùng trướcCách phục hồi an toàn
Reset hard nhầmgit reflog --date=isoTạo branch từ commit trước reset
Xóa branch localgit reflog --allTạo lại branch từ commit cuối
Rebase làm mất commitXem reflogTạo branch hoặc cherry-pick commit bị drop
Amend sai commitXem reflogSo sánh commit trước/sau amend rồi reset soft hoặc branch recovery
Reflog không còn entrygit fsck --lost-foundKiể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.

Checklist an toàn khi dùng git reflog để khôi phục commit
Recovery tốt bắt đầu bằng việc giảm thao tác phá hủy và tạo đường lui rõ ràng.

Nguồn tham khảo

  1. Git documentation: git-reflog
  2. Git documentation: git-reset
  3. Git documentation: git-cherry-pick
  4. CoreUI: How to recover deleted commit in Git
  5. Git documentation: git-gc
  6. Git documentation: git-fsck
  7. 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.reflogExpiregc.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é!

Tú Anh

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

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

Git commit nhầm file: bỏ file khỏi commit an toàn

Bài viết hướng dẫn xử lý git commit nhầm file theo từng tình huống: chưa commit, đã commit chưa push, đã push lên remote, hoặc lỡ

Git pull bị conflict: cách sửa không mất code

Bài viết hướng dẫn cách xử lý git pull bị conflict theo từng bước: kiểm tra trạng thái, sửa file xung đột, test lại và hoàn

Next.js production performance: chọn SSR, SSG, ISR hay Edge

Bài viết giúp developer và tech lead chọn cách render phù hợp để tối ưu Next.js production performance mà không làm kiến trúc phức tạp quá

Nâng cấp Laravel 13: Checklist 10 bước cần kiểm tra

Hướng dẫn nâng cấp Laravel 13 chi tiết với checklist 10 bước. Từ kiểm tra PHP 8.3, cập nhật dependencies, đến xử lý lỗi thường gặp

Hardening Laravel production: Checklist bảo mật cần làm

Checklist hardening Laravel production toàn diện. Từ cấu hình server, database, SSL đến security headers, rate limiting và monitoring.

Authentication và authorization trong Laravel: Cách phân biệt

Hướng dẫn chi tiết cách xây dựng hệ thống Authentication (xác thực) và Authorization (phân quyền) trong Laravel với Breeze, Fortify, Sanctum, Policies và Gates.

Bảo mật Laravel: 10 lỗi phổ biến và cách phòng tránh

Hướng dẫn 10 lỗi bảo mật phổ biến nhất trong Laravel và cách phòng tránh hiệu quả. Từ XSS, SQL injection đến authentication vulnerabilities.

Migration PHP Attributes Laravel 13: Hướng Dẫn Chi Tiết

Cách chuyển đổi từ protected properties sang PHP Attributes trong Laravel 13 với hướng dẫn từng bước và code examples chi tiết.

Laravel 13 có gì mới? Các thay đổi cần biết

Laravel 13 ra mắt ngày 17/3/2026 với PHP 8.3, PHP Attributes, AI SDK và nhiều cải tiến. Khám phá chi tiết các tính năng mới của

Kubernetes cho người mới: Hướng dẫn nhập môn

Kubernetes (K8s) là nền tảng container orchestration phổ biến nhất hiện nay. Bài hướng dẫn này sẽ giúp bạn hiểu Kubernetes là gì, kiến trúc cơ

Docker Compose best practices: 10 cách cấu hình tốt hơn

Docker Compose giúp bạn quản lý multi-container applications dễ dàng hơn. Bài viết này tổng hợp 10 best practices quan trọng nhất để sử dụng Docker

Lỗ hổng RCE (CVE-2025-55182) trên React, Next.js?

Cảnh báo khẩn cấp: React2Shell (CVE-2025-55182) gây RCE nghiêm trọng cho React/Next.js. Nắm cơ chế, dấu hiệu & phòng thủ cấp bách để bảo vệ ứng