Git recovery reset --hard
Bài đăng này đã không được cập nhật trong 8 năm
Một ngày đẹp trời khi đang làm việc, bạn dù vô tình hay cố ý chạy câu lệnh git reset --hard {commit}
. Xong! vậy là tất cả những gì bạn hùng hục làm cả ngày đã mất tiêu. Giờ là lúc chúng ta nghĩ về cách giải quyết hậu quả và khôi phục lại dữ liệu đã mất. Câu hỏi đặt ra là liệu dữ liệu có khôi phục lại được 100% hay không? Hãy xem xét các khả năng sau:
- 1. Nếu tất cả các file thay đổi đã được commit
May mắn là trường hợp này việc khôi phục lại dữ liệu khá đơn giản và nhanh gọn.
Trạng thái của repo trước khi reset hard:
git log --oneline
ff31686 fourth commit
ac79570 third commit
b6eb67c second commit
b20d416 first commit
Giả sử đã reset hard về second commit
bằng lệnh:
git reset --hard b6eb67c
Kết quả:
git log --oneline
b6eb67c second commit
b20d416 first commit
Để khôi phục lại dữ liệu thì cần quay lại commit fourth commit
, để reset HEAD về commit nào đó thì cơ bản chúng ta phải biết mã SHA1 của commit đó. Dùng lệnh git reflog
để liệt kê history của con trỏ HEAD:
git reflog
b6eb67c HEAD@{0}: reset: moving to b6eb67c
ff31686 HEAD@{1}: commit: fourth commit
ac79570 HEAD@{2}: reset: moving to HEAD@{1}
b6eb67c HEAD@{3}: reset: moving to b6eb67c
ac79570 HEAD@{4}: commit: third commit
b6eb67c HEAD@{5}: reset: moving to HEAD@{3}
b20d416 HEAD@{6}: reset: moving to HEAD@{1}
b6eb67c HEAD@{7}: checkout: moving from master to b6eb67c
b20d416 HEAD@{8}: reset: moving to b20d416
b6eb67c HEAD@{9}: commit: second commit
b20d416 HEAD@{10}: commit (initial): first commit
Check log tìm được mã SHA1 cuả fourth commit
. Tạo một nhánh để recovery dữ liệu:
git branch recover-branch ff31686
Check kết quả và thấy dữ liệu đã hoàn toàn được khôi phục:
git log --oneline
ff31686 fourth commit
ac79570 third commit
b6eb67c second commit
b20d416 first commit
- 2. Những thay đổi chưa được track và commit
Vì các file thay đổi chưa được track, tức bị git lờ đi nên khi lệnh git reset --hard {commit}
chúng không bị tác động. Do đó trường hợp này suy biến về như trường hợp 1. Các bước làm cũng tương tự.
- 3. Những thay đổi đã được add nhưng chưa được commit
Trạng thái hiện tại:
echo "file3 recovery" > file3.txt
git add .
git status
On branch recover-branch
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: file3.txt
Reset --hard:
git reset --hard HEAD
HEAD is now at ff31686 fourth commit
git status
On branch recover-branch
nothing to commit, working directory clean
Sau khi reset tất cả những thay đổi được tạo trong file3.txt đã biến mất, vì nó chưa được commit nên chúng ta không thể dùng reset HEAD về commit nào đó được.
Khi sử dụng lệnh git add
tức là thay đổi đã được đánh index, tạo thành objects và được lưu đâu đó trong repo tuy nhiên thay đổi này lại không được ref đến bởi commit nào. Trong git có một utility giúp chúng ta check được các objects không được tham chiếu tới (dangling
):
git fsck --full
Checking object directories: 100% (256/256), done.
dangling blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
dangling blob 2894e252ee1a560df965662a8c138b9e16e7dd3b
dangling blob e9e638322fe1703200d5af40e691af0208cf3a97
dangling blob 76dfd6c5ed7005dd04f626ad89291069992bf927
dangling blob fcfd9ad9834ed177ea525aec9473b2178099a64e
dangling blob 7d1b7da2833de3def992628293a159b56fab5a8d
dangling blob 3e65ed2670ae277e9d042659dd8639cd0f0f7d9c
Để check xem liệu object nào mà bạn đã làm mất trước khi reset --hard, sử dụng git show {sha1} để check nội dung. Sau khi check lần lượt từ dưới lên phát hiện một object:
git show 3e65ed2670ae277e9d042659dd8639cd0f0f7d9c
file3 recovery
file3 recovery
chính là nội dung đã thay đổi trước khi reset. Bạn có thể copy nội dung này tạo thành file mới để khôi phục lại dữ liệu.
Như vậy cách này gặp khó khăn với trường hợp nhiều file thay đổi bị mất vì phải lần lượt check nội dung của các object lơ lửng
hay dangling blob
rất mất thời gian và công sức.
- 4. Những thay đổi trong các file đã track từ trước đó mà chưa add
Đây là trường hợp thốn nhất
Trạng thái hiện tại:
git log --oneline
ff31686 fourth commit
ac79570 third commit
b6eb67c second commit
b20d416 first commit
Sau đó:
echo "hello" > file3.txt
ngocnv@ngocnv:~/workspace/gt_test$ git status
On branch recover-branch
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: file3.txt
Và sau đó reset --hard
git reset --hard
HEAD is now at ff31686 fourth commit
Xong! đến đây thì vô phương cứu chữa rồi. Vì bạn chưa đánh index cho những thay đổi này tức chưa git add
nên chúng sẽ chẳng được lưu ở đâu trên git cả nên sẽ không thể dùng git để recover chúng được. Nếu may mắn bạn đang sử dụng một IDE có chức năng xem local history thì mới có thể khôi phục lại chúng.
Đây cũng là trường hợp để chúng ta cân nhắc khi sử dụng git reset --hard
. Thay vì sử dụng option --hard
tùy vào mục đích mà bạn có thể sử dụng các option khác bớt nguy hiểm hơn như: --soft
, --mixed
, --keep
, --merge
...
All rights reserved