Làm cách nào tôi có thể khôi phục cam kết bị mất trong Git?


258

Đầu tiên, nhận được "chi nhánh của bạn đi trước nguồn gốc / chủ nhân bằng 3 lần cam kết" thì ứng dụng của tôi đã trở lại thời điểm sớm hơn với những thay đổi trước đó.

Làm thế nào tôi có thể nhận được những gì tôi đã dành 11 giờ qua để làm lại?


Câu trả lời:


587

git refloglà bạn của bạn. Tìm cam kết mà bạn muốn có trong danh sách đó và bạn có thể đặt lại cho nó (ví dụ git reset --hard e870e41:).

(Nếu bạn không cam kết thay đổi của mình ... bạn có thể gặp rắc rối - hãy cam kết sớm và cam kết thường xuyên!)


5
Hãy nhìn vào git log HEAD@{1}. Nếu đó trông giống như một loạt các cam kết, thì bạn có thể git reset HEAD@{1}.
Amber

4
Chỉ khi các mã được dàn dựng (sử dụng git add), chúng sẽ được giữ trong git và có thể được tìm lại dễ dàng bằng các lệnh như git fsck --lost-found.
Landys

2
Vô tình làm rơi một cam kết mà tôi nên giữ khi nổi loạn. Điều này hoàn toàn cứu tôi khỏi làm lại một vài giờ làm việc.
josephting

27
Điều này đã cứu cuộc đời tôi.
Lutaaya Huzaifah Idris

6
Điều này đã cứu tôi tỉnh táo: D
Frank Fajardo

118

Trước khi trả lời, hãy thêm một số nền tảng, giải thích đây HEADlà gì .

First of all what is HEAD?

HEADchỉ đơn giản là một tham chiếu đến cam kết hiện tại (mới nhất) trên nhánh hiện tại.
Chỉ có thể có một lần duy nhất HEADtại bất kỳ thời điểm nào (không bao gồm git worktree).

Nội dung của HEADđược lưu trữ bên trong .git/HEADvà nó chứa 40 byte SHA-1 của cam kết hiện tại.


detached HEAD

Nếu bạn không tham gia vào cam kết mới nhất - có nghĩa HEADlà chỉ đến một cam kết trước đó trong lịch sử, nó được gọi detached HEAD.

Nhập mô tả hình ảnh ở đây

Trên dòng lệnh, nó sẽ trông như thế này - SHA-1 thay vì tên nhánh vì HEADnó không trỏ đến đầu của nhánh hiện tại:

Nhập mô tả hình ảnh ở đây

Nhập mô tả hình ảnh ở đây


Một vài lựa chọn về cách phục hồi từ một ĐẦU tách ra:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Điều này sẽ kiểm tra chi nhánh mới chỉ vào cam kết mong muốn.
Lệnh này sẽ kiểm tra một cam kết nhất định.
Tại thời điểm này, bạn có thể tạo một nhánh và bắt đầu làm việc từ thời điểm này.

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Bạn luôn có thể sử dụng refloglà tốt.
git reflog sẽ hiển thị bất kỳ thay đổi nào đã cập nhật HEADvà kiểm tra mục nhập reflog mong muốn sẽ đặt HEADlại cho cam kết này.

Mỗi khi sửa đổi ĐẦU sẽ có một mục mới trong reflog

git reflog
git checkout HEAD@{...}

Điều này sẽ đưa bạn trở lại cam kết mong muốn của bạn

Nhập mô tả hình ảnh ở đây


git reset --hard <commit_id>

"Di chuyển" ĐẦU của bạn trở lại cam kết mong muốn.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Lưu ý: ( Kể từ Git 2.7 ), bạn cũng có thể sử dụng git rebase --no-autostash.

git revert <sha-1>

"Hoàn tác" phạm vi cam kết hoặc cam kết đã cho.
Lệnh đặt lại sẽ "hoàn tác" mọi thay đổi được thực hiện trong cam kết đã cho.
Một cam kết mới với bản vá hoàn tác sẽ được cam kết trong khi cam kết ban đầu cũng sẽ vẫn còn trong lịch sử.

# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Lược đồ này minh họa lệnh nào làm gì.
Như bạn có thể thấy ở đó, reset && checkoutsửa đổi HEAD.

Nhập mô tả hình ảnh ở đây


2
Bạn là một anh hùng, thưa ngài
dylanh724

4
Điều đó chỉ giúp tôi tiết kiệm nhiều giờ làm việc. Tôi đã sử dụng "git reflog --date = iso" để xem ngày / giờ cho mỗi mục vì tôi không thể biết chắc chắn nếu không có dấu thời gian.
MetalMikester

1
cho tôi, trong git reset --hard <commit_id>, loại bỏ HEADlàm việc! +1 cho biểu diễn đồ họa !!.
Reverie_ss

Chỉ cần đề cập: nếu bạn biết tên chi nhánh: git reflog <branchname>có thể khá hữu ích, vì bạn thấy những thay đổi của chỉ một chi nhánh.
Markus Schreiber

35

Một cách khác để có được cam kết bị xóa là với git fscklệnh.

git fsck --lost-found

Điều này sẽ tạo ra một cái gì đó giống như ở dòng cuối cùng:

dangling commit xyz

Chúng tôi có thể kiểm tra xem đó có phải là cùng một cam kết sử dụng reflognhư được đề xuất trong các câu trả lời khác không. Bây giờ chúng ta có thể làm mộtgit merge

git merge xyz

Lưu ý:
Chúng tôi không thể lấy lại cam kết fscknếu chúng tôi đã chạy một git gclệnh sẽ xóa tham chiếu đến cam kết lơ lửng.


3
Đây là câu trả lời duy nhất hoạt động nếu gần đây bạn chỉ vào cam kết trong câu hỏi, chẳng hạn như khi bạn tìm nạp một nhánh và sau đó vô tình đặt lại nhánh đó ở nơi khác.
TamaMcGlinn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.