Câu hỏi Tìm và khôi phục tệp đã xóa trong kho lưu trữ Git


Giả sử tôi đang ở trong kho lưu trữ Git. Tôi xóa một tập tin và cam kết thay đổi đó. Tôi tiếp tục làm việc và thực hiện thêm một số cam kết. Sau đó, tôi thấy tôi cần phải khôi phục tập tin đó.

Tôi biết tôi có thể kiểm tra tệp bằng git checkout HEAD^ foo.bar, nhưng tôi thực sự không biết khi nào tập tin đó bị xóa.

  1. Điều gì sẽ là cách nhanh nhất để tìm thấy cam kết đã xóa một tên tập tin nhất định?
  2. Cách dễ nhất để đưa tệp đó trở lại bản sao làm việc của tôi là gì?

Tôi hy vọng tôi không phải tự duyệt các bản ghi của tôi, kiểm tra toàn bộ dự án cho một SHA nhất định và sau đó tự sao chép tệp đó vào thanh toán dự án ban đầu của tôi.


2386
2018-06-04 22:40


gốc


$ git checkout deletedFile, không ai rõ ràng đã nói điều này ?! Trả lời cho tiêu đề cho các googlers trong tương lai ... - hhh
lưu ý rằng nhận xét trước đó trả lời câu hỏi trong tiêu đề, chứ không phải trong nội dung - bao gồm việc tìm ra khi nào tệp đã bị xóa. - avdgaag
Để tìm commit, một tập tin đã bị xóa trong: git log --diff-filter=D -- path/to/file - titaniumdecoy
Liên quan: Làm thế nào để bạn loại bỏ những thay đổi không được tổ chức trong git?.
@hhh git checkout deletedFile sẽ phục hồi deletedFile nếu nó đã bị xóa nhưng việc xóa đó chưa được tổ chức hoặc cam kết. Đó không phải là câu hỏi ở đây là yêu cầu; câu hỏi này là về cách khôi phục một tập tin mà xóa đã được cam kết nhiều cam kết trước đây. - Mark Amery


Các câu trả lời:


Tìm cam kết cuối cùng ảnh hưởng đến đường dẫn đã cho. Vì tệp không có trong cam kết HEAD, cam kết này phải xóa nó.

git rev-list -n 1 HEAD -- <file_path>

Sau đó, kiểm tra phiên bản tại cam kết trước, sử dụng dấu mũ (^) ký hiệu:

git checkout <deleting_commit>^ -- <file_path>

Hoặc trong một lệnh, nếu $file là tệp được đề cập.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Nếu bạn đang sử dụng zsh và đã bật tùy chọn EXTENDED_GLOB, ký hiệu dấu mũ sẽ không hoạt động. Bạn có thể dùng ~1 thay thế.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"

2725
2017-07-11 07:12



Bit khéo léo là để kiểm tra các cam kết TRƯỚC KHI, sử dụng ^ hậu tố. Cảm ơn. - Christian Oudard
^ Cuối cùng là gì? - ranman
@Ranman: Nó có nghĩa là "cha mẹ đầu tiên của". - CB Bailey
Từ cửa sổ dòng lệnh tôi đã nhận một lỗi. error: pathspec <filename> did not match any file(s) known to git.. Giải pháp là sử dụng git bash. - donturner
@zoras zsh có sự mở rộng của riêng nó trên '^' tôi tin, nhưng bạn có thể sử dụng cú pháp thay thế của '~ 1': git checkout <deleting-commit>~1 -- <file-path>  ~ X cho phép bạn chỉ định X cam kết trước cam kết được chỉ định, vì vậy ~ 1 là cam kết trước, ~ 2 là hai lần commit trước, v.v. - Nils Luxton


  1. Sử dụng git log --diff-filter=D --summary để có được tất cả các cam kết đã xóa các tập tin và các tập tin bị xóa;
  2. Sử dụng git checkout $commit~1 filename để khôi phục tệp đã xóa.

Ở đâu $commit là giá trị của cam kết bạn đã tìm thấy ở bước 1, ví dụ: e4cf499627


724
2018-06-04 23:10



tò mò, những gì ~ 1 đề cập đến? - tommy chheng
@tommy - thông số dấu ngã sẽ cung cấp cho bạn cháu thứ n của cam kết có tên. Xem book.git-scm.com/4_git_treeishes.html để biết thêm chi tiết . - Robert Munteanu
đây là cách tiếp cận dễ dàng và trực quan nhất. git log -- *PartOfMyFileName*. Cảm ơn vì $commit~1 - bgs
các git checkout $commit~1 filename cú pháp hoạt động hoàn hảo cho các tệp riêng lẻ và cũng hoạt động cho toàn bộ thư mục. tức là: để khôi phục tất cả các hình ảnh đã xóa trong ./images từ sha 12345: git checkout 12345~1 images. cảm ơn câu trả lời này! - noinput
@Alexar $commit~1 có nghĩa là bạn nên thêm tên của cam kết. Cái gì đó như 1d0c9ef6eb4e39488490543570c31c2ff594426c Ở đâu $commit Là. - Eugene


Để khôi phục tất cả các tệp đã xóa trong một thư mục, hãy nhập lệnh sau.

git ls-files -d | xargs git checkout --

302
2017-12-02 06:11



Các tệp được chuyển đến đâu? Tôi không thấy thay đổi. - William Grand
Đây có lẽ là phương pháp dễ nhất. Biến thái của nó khó khăn như thế nào git đã thực hiện ngay cả nhiệm vụ đơn giản nhất. - jww
có nghĩa là gì -? - Tebe
git checkout - [file] sẽ hoàn nguyên các thay đổi trong [tệp]. Đường ống sẽ thay thế [tệp] bằng tên của các tệp đã xóa. - Manu
Các ls-files lệnh phụ có ích, nhưng dường như không hoạt động đối với các tệp đã bị xóa git rm tức là dàn dựng, hãy để một mình cam kết, đó là những gì OP hỏi. - MarkHu


Tôi đã đến câu hỏi này để khôi phục một tệp tôi vừa xóa nhưng tôi chưa cam kết thay đổi. Chỉ trong trường hợp bạn thấy mình trong tình huống này, tất cả những gì bạn cần làm là như sau:

git checkout HEAD -- path/to/file.ext


100
2018-04-10 00:03



Cảm ơn. Nó làm việc cho tôi. Khôi phục tệp mà tôi chưa cam kết thay đổi. - Hua Zhang
Điều này đơn giản và tốt nhất. - SmallChess


Nếu bạn điên, hãy sử dụng git-bisect. Đây là những việc cần làm:

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

Bây giờ là lúc để chạy thử nghiệm tự động. Lệnh shell '[ -e foo.bar ]' sẽ trả về 0 nếu foo.bar tồn tại và 1 cách khác. Lệnh "chạy" của git-bisect sẽ sử dụng tìm kiếm nhị phân để tự động tìm commit đầu tiên trong đó kiểm tra thất bại. Nó bắt đầu giữa chừng (từ tốt đến xấu) và cắt giảm một nửa dựa trên kết quả của phép thử được chỉ định.

git bisect run '[ -e foo.bar ]'

Bây giờ bạn đang ở cam kết xóa nó. Từ đây, bạn có thể quay lại tương lai và sử dụng git-revert để hoàn tác thay đổi,

git bisect reset
git revert <the offending commit>

hoặc bạn có thể quay lại một lần commit và tự kiểm tra sát thương:

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .

82
2018-06-04 22:46



Bạn có thể xây dựng trên git bisect run '[ -e foo.bar ]'? - avdgaag
Bạn cũng có thể sử dụng tốt và xấu theo cách thủ công, nếu đó là thứ không thể kiểm tra tự động. Xem trang bisect man. - Josh Lee
Nó không phải là giải pháp dễ dàng nhất, nhưng nó khá ấn tượng. Cảm ơn đã viết lên. - avdgaag
@avdgaag git bisect run bảo Git tự động bisection bằng cách chạy lệnh sau từ 'run' trong đó lệnh phải trả về 0 cho một good phiên bản (xem git help bisect để biết chi tiết). Các '[ -e foo.bar ]' là một biểu thức chuẩn để kiểm tra nếu tệp foo.bar không tồn tại (việc triển khai thường nằm trong tệp /usr/bin/[ thường được liên kết cứng đến /usr/bin/test) và các dấu ngoặc đơn được sử dụng để đặt tất cả đó là một đối số dòng lệnh. - Mikko Rantalainen


Bí danh yêu thích mới của tôi, dựa trên bonyiii'S câu trả lời (upvoted) và câu trả lời của riêng tôi về "Chuyển đối số cho lệnh bí danh Git":

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

Tôi đã mất một tập tin, bị xóa do nhầm lẫn một vài cam kết trước đây?
Nhanh chóng:

git restore my_deleted_file

Ngăn chặn khủng hoảng.


Robert Dailey đề xuất trong các bình luận bí danh sau:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

jegan bổ sung trong các bình luận:

Để thiết lập bí danh từ dòng lệnh, tôi đã sử dụng lệnh này:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 

61
2018-02-17 15:33



Điều này khôi phục toàn bộ cam kết, không chỉ các tập tin được yêu cầu. - Daniel Bang
Đây là bí danh của tôi, hoạt động tuyệt vời: restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1" - void.pointer
@RobertDailey Điều đó có vẻ tuyệt vời! Tôi đã đưa bí danh của bạn vào câu trả lời để hiển thị nhiều hơn. - VonC
git: 'restore' không phải là lệnh git. - resultsway
Để thiết lập bí danh từ dòng lệnh, tôi đã sử dụng lệnh này: git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" - jegan


Nếu bạn biết tên tệp, đây là cách dễ dàng với các lệnh cơ bản:

Liệt kê tất cả các cam kết cho tệp đó.

git log -- path/to/file

Cam kết cuối cùng (trên cùng) là thư đã xóa tệp. Vì vậy, bạn cần phải khôi phục lại thứ hai để cam kết cuối cùng.

git checkout {second to last commit} -- path/to/file

42
2018-02-27 01:50



Vâng. Đây là giải pháp đúng cho tôi! Đơn giản và rõ ràng - Fabrizio Bertoglio
Chỉ cần sử dụng giải pháp này và không có cam kết xóa bỏ. Tôi đã có thể khôi phục tập tin bằng cách sử dụng các cam kết gần đây nhất mặc dù id. - Adam
+10 cho second to last commit làm rõ! - colminator
Không phải là cam kết tiếp theo (cam kết trước đó để xóa) có chứa phiên bản mới nhất của tệp đã xóa không? Thứ hai-to-last (cam kết trước khi cam kết trước đó để xóa) có thể là vô vọng đã lỗi thời. - Suncat2000
Đây là giải pháp đầu tiên tôi đã thấy đơn giản là tôi sẽ không phải quay lại đây để tìm nó lần sau. Có lẽ. - Eloff


Để khôi phục tệp đã xóa và đã cam kết:

git reset HEAD some/path
git checkout -- some/path

Nó đã được thử nghiệm trên phiên bản Git 1.7.5.4.


27
2017-07-04 04:39



Điều đó không hiệu quả với tôi. Sau khi thanh toán, tôi đã nhận error: pathspec 'foo' did not match any file(s) known to git. Tôi đảm bảo rằng tên tệp là chính xác. Phiên bản Git 2.7.0 - wisbucky
-1; cái này sai. Các lệnh này sẽ hoàn tác thao tác xóa chưa được cam kết (trường hợp đầu tiên bỏ đoạn xóa, nếu nó được dàn dựng và phần thứ hai loại bỏ các thay đổi chưa được chỉnh sửa đối với tệp), nhưng bạn đang xác nhận quyền sở hữu ở đây rằng chúng sẽ khôi phục cam kết xóa tập tin, mà chỉ đơn giản là không đúng sự thật và sẽ thất bại với một lỗi như vậy trong bình luận @ wisbucky ở trên. - Mark Amery
@MarkAmery Thật vậy, tôi nghĩ rằng lệnh này hoạt động tốt cho những nhà phát triển, những người đã không thực hiện dàn dựng rõ ràng để cam kết xóa các tệp với git add -A, nhưng vì vậy tệp đã khôi phục vẫn ở giai đoạn không cam kết. - Fedir RYKHTIK


Nếu bạn chỉ thực hiện thay đổi và xóa tệp, nhưng không cam kết, và bây giờ bạn đã chia tay với những thay đổi của mình

git checkout -- .

nhưng các tệp đã xóa của bạn không trả về, bạn chỉ cần thực hiện lệnh sau:

git checkout <file_path>

Và mau, tập tin của bạn đã trở lại.


21
2017-09-02 15:30





tôi có giải pháp này.

  1. Nhận id của cam kết trong đó tệp đã bị xóa bằng cách sử dụng một trong các cách bên dưới.

    • git log --grep=*word* 
    • git log -Sword
    • git log | grep --context=5 *word*
    • git log --stat | grep --context=5 *word* # được đề xuất nếu bạn khó nhớ bất cứ điều gì
  2. Bạn sẽ nhận được một cái gì đó như:

cam kết bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Tác giả: Alexander   Orlov Ngày: Thứ Năm 12 Tháng Năm 23:44:27 2011   +0200

replaced deprecated GWT class
- gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script

cam kết 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Tác giả: Alexander   Orlov Ngày: Thứ Năm 12 Tháng Năm 22:10:22 2011   +0200

3. Bây giờ, sử dụng id cam kết bfe68bd117e1091c96d2976c99b3bcc8310bebe7 làm:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java

Khi id cam kết tham chiếu đến cam kết trong đó tệp đã bị xóa, bạn cần tham khảo cam kết ngay trước bfe68b mà bạn có thể thực hiện bằng cách thêm ^1. Điều này có nghĩa là: cho tôi cam kết ngay trước bfe68b.


20
2018-03-01 10:48



Đây là phương pháp tương tự như câu trả lời được chấp nhận, nhưng với một số cách khác để tìm ra cam kết xóa. Tôi vẫn thích cách tiếp cận được đưa ra trong câu trả lời được chấp nhận, nhưng đây là những lựa chọn thay thế tốt. Cảm ơn! - avdgaag
Tôi giả định rằng đầu tiên kiểm tra các tập tin đã xóa và sau đó (mà không thay đổi nó) cam kết nó không tạo ra một sao chép của tệp. Đúng? (Tôi cần phải làm điều này với hình ảnh, và một bản sao sẽ làm cho kho lưu trữ lớn hơn) - Stonecrusher