[Git nâng cao] Git reflog: Phục hồi lịch sử commit và mọi thao tác trong Git

VnnTools

Chắc hẳn bất kỳ lập trình viên nào khi làm việc với Git cũng đã từng trải qua cảm giác "đứng tim" 😱: lỡ tay xóa một nhánh quan trọng, reset --hard nhầm một commit tâm huyết, hay thực hiện một pha rebase "đi vào lòng đất". Những lúc như vậy, bạn ước gì có một cỗ máy thời gian để quay lại? Tin vui là Git đã trang bị sẵn cho bạn một "cỗ máy" như thế, và nó mang tên Git Reflog.

Git reflog: Phục hồi lịch sử commit và mọi thao tác trong Git

Bài viết này sẽ giúp bạn hiểu rõ "Git Reflog là gì", tại sao nó là cứu tinh của mọi lập trình viên và làm thế nào để sử dụng nó một cách hiệu quả nhất.

Git Reflog: "Cuốn nhật ký bí mật" của HEAD 📝

Để dễ hình dung hơn, bạn hãy tưởng tượng git log giống như biên niên sử chính thức của dự án, ghi lại những cột mốc quan trọng (các commit) đã được chia sẻ với mọi người. Nó công khai, tuần tự và là một phần của lịch sử chung.

Ngược lại, git reflog (viết tắt của "reference log") giống như cuốn nhật ký cá nhân của bạn. Nó không quan tâm đến lịch sử "chính thống" của dự án, mà chỉ âm thầm ghi lại mọi hành động làm thay đổi vị trí của con trỏ HEAD trên máy local của bạn. Mỗi khi bạn commit, checkout, reset, merge, rebase hay bất kỳ lệnh nào khác di chuyển HEAD, reflog đều ghi lại một dòng: "Tôi vừa ở đây, và bây giờ tôi đã đi đến đây".

Đây chính là điểm khác biệt cốt lõi:

  • git log: Ghi lại lịch sử của các commit trong dự án. Lịch sử này được chia sẻ khi bạn push.
  • git reflog: Ghi lại lịch sử di chuyển của HEAD chỉ trên máy của bạn. Nó là cục bộ và không được đẩy lên remote repository.

Chính vì tính chất "cá nhân" và "ghi lại tất cả" này mà reflog trở thành một tấm lưới an toàn vô giá. Kể cả khi một commit không còn được trỏ đến bởi bất kỳ nhánh hay tag nào (thường được gọi là commit "mồ côi" hay "bị mất"), nó vẫn tồn tại trong reflog.

Tại sao Reflog là "bảo bối" của lập trình viên? 🦸‍♂️

Sức mạnh thực sự của git reflog nằm ở khả năng khôi phục lại những thứ tưởng chừng đã mất. Dưới đây là những kịch bản "ác mộng" mà reflog có thể giải cứu bạn.

Kịch bản 1: Lỡ tay xóa mất một nhánh

Bạn đang dọn dẹp các nhánh cũ và lỡ tay dùng lệnh git branch -D feature-moi-lam-ca-tuan. Mọi công sức của cả tuần dường như tan biến. Đừng hoảng sợ!

  1. Mở "cuốn nhật ký": Gõ lệnh git reflog.

  2. Tìm lại dấu vết: Bạn sẽ thấy một danh sách các hành động gần đây. Hãy tìm dòng ngay trước khi bạn checkout ra khỏi nhánh đã xóa. Nó sẽ trông giống như checkout: moving from feature-moi-lam-ca-tuan to main.

  3. Hồi sinh nhánh: Lấy mã hash của commit đó (ví dụ: a1b2c3d) và tạo lại nhánh một cách diệu kỳ:

    git checkout -b feature-moi-lam-ca-tuan a1b2c3d
    

Hola! Nhánh của bạn đã trở lại nguyên vẹn như chưa hề có cuộc chia ly.

Kịch bản 2: "Reset --hard" và cái kết đắng 😭

Đây có lẽ là tình huống kinh điển nhất. Bạn muốn quay lại vài commit trước và không ngần ngại gõ git reset --hard HEAD~3. Sau khi thực hiện xong, bạn nhận ra mình đã xóa nhầm 3 commit vô cùng quan trọng. git log lúc này sẽ không còn thấy chúng nữa.

Cứu tinh git reflog lại xuất hiện:

  1. git reflog.

  2. Bạn sẽ thấy lịch sử các vị trí của HEAD. Các commit mà bạn vừa "vứt đi" sẽ nằm ngay ở đầu danh sách, thường được đánh dấu là HEAD@{1}, HEAD@{2}...

  3. Quay ngược thời gian: Giả sử commit cuối cùng bạn muốn lấy lại có mã là e4f5g6h (HEAD@{1}). Hãy reset lại về đúng trạng thái đó:

    git reset --hard e4f5g6h
    

Tất cả code của bạn đã được khôi phục một cách an toàn.

Kịch bản 3: Rebase "thảm họa"

Bạn thực hiện một pha rebase phức tạp và kết quả là lịch sử commit của bạn trở thành một mớ hỗn độn, xung đột khắp nơi. Bạn chỉ muốn quay lại trạng thái "an toàn" trước khi rebase.

reflog đã ghi lại điểm bắt đầu của cuộc rebase.

  1. Chạy git reflog.

  2. Tìm đến dòng có ghi rebase: checkout ORIG_HEAD hoặc tương tự. Vị trí ngay trước đó chính là nơi bạn bắt đầu.

  3. Lấy mã hash của commit đó và quay trở lại:

    git reset --hard <commit-hash-truoc-khi-rebase>
    

Cách sử dụng Git Reflog hiệu quả

Lệnh cơ bản nhất là git reflog hoặc git reflog show. Kết quả trả về có dạng:

a1b2c3d HEAD@{0}: commit: Thêm tính năng thanh toán
e4f5g6h HEAD@{1}: reset: moving to HEAD~3
f0g9h8i HEAD@{2}: commit: Tối ưu hóa giao diện người dùng
...

Giải thích các thành phần:

  • a1b2c3d: Mã hash của commit (SHA-1).
  • HEAD@{0}: Đây là con trỏ của reflog. HEAD@{0} là vị trí gần nhất của HEAD (tức là hiện tại), HEAD@{1} là vị trí trước đó, HEAD@{2} là trước đó nữa...
  • commit: Thêm tính năng thanh toán: Hành động đã khiến HEAD di chuyển và thông điệp commit đi kèm.

Bạn có thể sử dụng các con trỏ này trực tiếp trong các lệnh Git khác:

# Checkout về trạng thái của HEAD 2 bước trước
git checkout HEAD@{2}

# So sánh sự khác biệt giữa trạng thái hiện tại và 5 bước trước
git diff HEAD@{5} HEAD@{0}

# Cherry-pick một commit từ reflog vào nhánh hiện tại
git cherry-pick a1b2c3d

Lưu ý quan trọng: Reflog không tồn tại mãi mãi!

reflog là một cơ chế dọn dẹp. Theo mặc định, các mục trong reflog sẽ tự động bị xóa sau 90 ngày. Điều này có nghĩa là bạn không thể dùng nó để khôi phục những thay đổi từ quá lâu. Thời gian này có thể được cấu hình, nhưng 90 ngày thường là đủ cho hầu hết các trường hợp "cấp cứu".

Kết luận: Hãy coi Reflog là bạn đồng hành

Dù cho git reflog không phải là lệnh bạn dùng hàng ngày, nhưng vào những thời điểm khó khăn, nó chính là người bạn đồng hành đáng tin cậy nhất. Nó cho bạn sự tự tin để thử nghiệm, để mắc lỗi và để biết rằng luôn có một con đường quay trở lại.

Hãy tập thói quen khi gặp sự cố, trước khi hoảng loạn, hãy bình tĩnh gõ git reflog và xem lại "cuốn nhật ký" của mình. Rất có thể, giải pháp cho vấn đề của bạn đang nằm ngay ở những dòng log đầu tiên. Chúc bạn code an toàn và không bao giờ "mất" commit nữa! 🚀

Bài viết liên quan

[Git nâng cao] Git Reset hay Git Revert? Lựa chọn hoàn tác đúng cách

Không còn nhầm lẫn giữa git reset và git revert nữa! Hướng dẫn này sẽ giúp bạn hiểu rõ cách hoạt động của từng lệnh, từ đó lựa chọn công cụ đúng đắn để hoàn tác thay đổi trong Git một cách chuyên nghiệp.

[Git cơ bản] Git Branch là gì? Hiểu rõ và làm chủ phân nhánh trong Git

Branch là một tính năng quan trọng giúp chia nhỏ dự án trong Git. Đọc bài viết để hiểu rõ về Git Branch, cách tạo, xóa, và chuyển đổi giữa các nhánh một cách dễ dàng.

[Git cơ bản] Cách dùng Git log: Nắm lịch sử commit trong lòng bàn tay

Bạn muốn xem lại lịch sử thay đổi của dự án? Hướng dẫn chi tiết cách dùng lệnh git log từ cơ bản đến nâng cao, giúp bạn quản lý các commit hiệu quả và dễ dàng hơn.

[Git nâng cao] Hướng dẫn Git Rebase: Hiểu rõ và sử dụng hiệu quả

Git Rebase dùng để sắp xếp lại, kết hợp và thay đổi các commit. Bài viết này sẽ giúp bạn phân biệt Git Rebase và Git Merge, đồng thời nắm vững cách dùng Git Rebase hiệu quả, an toàn.