Câu hỏi Làm thế nào để phục hồi một stash giảm trong Git?


Tôi thường xuyên sử dụng git stash và git stash pop để lưu và khôi phục các thay đổi trong cây đang hoạt động của tôi. Hôm qua tôi đã có một số thay đổi trong cây làm việc của mình mà tôi đã cất giấu và xuất hiện, và sau đó tôi đã thực hiện nhiều thay đổi hơn cho cây làm việc của mình. Tôi muốn quay lại và xem lại những thay đổi đã được lưu trữ của ngày hôm qua, nhưng git stash pop xuất hiện để xóa tất cả các tham chiếu đến cam kết được liên kết.

Tôi biết rằng nếu tôi sử dụng git stash sau đó .git / refs / stash chứa tham chiếu của cam kết được sử dụng để tạo stash. Và .git / logs / refs / stash chứa toàn bộ stash. Nhưng những tài liệu tham khảo đó biến mất sau git stash pop. Tôi biết rằng cam kết vẫn còn trong kho của tôi ở đâu đó, nhưng tôi không biết nó là gì.

Có cách nào dễ dàng để khôi phục lại tham chiếu cam kết của ngày hôm qua?

Lưu ý rằng điều này không quan trọng đối với tôi ngày hôm nay bởi vì tôi có bản sao lưu hàng ngày và có thể quay lại cây làm việc của ngày hôm qua để nhận các thay đổi của tôi. Tôi hỏi vì phải có một cách dễ dàng hơn!


1334
2017-09-18 01:59


gốc


Lưu ý cho tương lai: Nếu bạn không muốn để mất stashes của bạn mỗi khi bạn git stash pop, bạn có thể làm git stash apply thay thế. Nó làm điều tương tự, ngoại trừ nó không loại bỏ tham chiếu đến stash được áp dụng. - Kevin
Hãy rất cẩn thận khi bạn sử dụng stash trong khi sửa đổi - xem điều này - Mr_and_Mrs_D
Cố gắng tất cả mọi thứ ở đây, không thể tìm thấy một stash đã được popped rồi. Rất vui vì IntelliJ's jetbrains.com/help/idea/local-history.html - Juan Mendes


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


Nếu bạn chỉ có popped nó và các thiết bị đầu cuối vẫn còn mở, bạn sẽ vẫn có giá trị băm được in bởi git stash pop trên màn hình (cảm ơn, Dolda).

Nếu không, bạn có thể tìm thấy nó bằng cách sử dụng nó cho Linux và Unix:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

và cho Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}

Điều này sẽ cho bạn thấy tất cả các cam kết ở đầu của biểu đồ cam kết không còn được tham chiếu từ bất kỳ chi nhánh hoặc thẻ nào - mọi cam kết bị mất, bao gồm mọi cam kết stash bạn từng tạo, sẽ ở đâu đó trong biểu đồ đó.

Cách dễ nhất để tìm thấy dấu gạch chéo mà bạn muốn có lẽ là chuyển danh sách đó đến gitk:

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

Thao tác này sẽ khởi chạy trình duyệt kho lưu trữ cho bạn thấy mọi cam kết đơn lẻ trong kho lưu trữ, bất kể nó có thể truy cập được hay không.

Bạn có thể thay thế gitk có cái gì đó như git log --graph --oneline --decorate nếu bạn thích một biểu đồ tốt đẹp trên bảng điều khiển trên một ứng dụng GUI riêng biệt.

Để phát hiện các cam kết stash, hãy tìm thư cam kết của biểu mẫu này:

WIP trên somebranch: commithash Một số thông điệp cam kết cũ

chú thích: Thông báo cam kết sẽ chỉ ở dạng này (bắt đầu bằng "WIP on") nếu bạn không cung cấp thông báo khi bạn đã làm git stash.

Một khi bạn biết băm của cam kết bạn muốn, bạn có thể áp dụng nó như là một stash:

git stash apply $ stash_hash

Hoặc bạn có thể sử dụng menu ngữ cảnh trong gitk để tạo ra các nhánh cho bất kỳ cam kết không thể truy cập nào mà bạn quan tâm. Sau đó, bạn có thể làm bất cứ điều gì bạn muốn với chúng với tất cả các công cụ bình thường. Khi bạn hoàn thành, chỉ cần thổi những nhánh đó đi một lần nữa.


2159
2017-09-18 11:38



Jaydel lấy lời ra khỏi miệng tôi. Bài đăng này đã lưu công việc của tôi :) Tôi chỉ muốn thêm - ghi nhớ ngày bạn làm việc trên bất kỳ thứ gì bạn đã mất làm cho việc duyệt gitk dễ dàng hơn cho những gì bạn đang tìm kiếm. - Sridhar-Sarnobat
@Codey: Vì PowerShell. Tôi không biết nếu MsysGit gửi một nhị phân AWK. Googling nói với tôi rằng một cái gì đó như %{ $_.Split(' ')[2]; } nên làm tương đương với {print $3} trong đó awk trong PowerShell, nhưng tôi không có hệ thống Windows để kiểm tra điều đó và bạn vẫn cần tương đương với /dangling commit/phần. Dù sao, chỉ cần chạy git fsck --no-reflog và nhìn vào đầu ra. Bạn muốn băm từ các dòng “dangling commit <commitID>”. - Aristotle Pagaltzis
Lệnh tuyệt vời. Xem câu trả lời của tôi cho phiên bản PowerShell: stackoverflow.com/a/34666995/219072 - emragins
Điều đáng nói đến là thông báo cam kết sẽ chỉ có chuỗi "WIP" nếu bạn không cung cấp thông điệp của riêng bạn khi stashing (tức là bằng cách thực hiện git stash save "<message>"). - Samir Aguiar
Nếu bạn biết khi nào sự sụt giảm xảy ra, bạn có thể sử dụng một lớp lót này để có được danh sách các cam kết lơ lửng bằng cách tăng thời gian: git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sort mục cuối cùng có lẽ là mục bạn muốn stash apply. - ris8_allo_zen0


Nếu bạn không đóng thiết bị đầu cuối, chỉ cần nhìn vào đầu ra từ git stash pop và bạn sẽ có ID đối tượng của dấu gạch chéo bị bỏ. Nó thường trông như thế này:

$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Lưu ý rằng git stash drop cũng tạo ra cùng một dòng.)

Để lấy lại thứ đó, chỉ cần chạy git branch tmp 2cae03evà bạn sẽ lấy nó như một nhánh. Để chuyển đổi thành stash, hãy chạy:

git stash apply tmp
git stash

Có nó như là một chi nhánh cũng cho phép bạn thao tác nó một cách tự do; ví dụ, để anh đào chọn hoặc hợp nhất nó.


598
2017-10-21 03:13



Đây thường là câu trả lời đơn giản nhất và xứng đáng là cao hơn trong danh sách - Casebash
Bạn cũng có thể làm git stash apply commitid sau đó git stash để có được một stash mới. - Matthew Flaschen
Lưu ý rằng nếu git tự động hợp nhất stash và có xung đột, nó sẽ không hiển thị cho bạn băm. - James
@ James: Sau đó, một lần nữa, nếu những xung đột đó là kết quả của việc chạy git stash pop, nó cũng sẽ không rơi xuống, vì vậy đó không phải là vấn đề. - Dolda2000
Tôi đã làm một cú đánh stit git và nghĩ rằng những thay đổi của tôi đã bị mất vĩnh cửu. Câu trả lời này đã cứu tôi :) điều này cần được bình chọn nhiều hơn! - raghav710


Chỉ muốn đề cập đến bổ sung này vào giải pháp được chấp nhận. Nó không phải là ngay lập tức rõ ràng với tôi lần đầu tiên tôi đã thử phương pháp này (có lẽ nó nên có được), nhưng để áp dụng các stash từ giá trị băm, chỉ cần sử dụng "git stash áp dụng":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Khi tôi còn mới với git, điều này không rõ ràng với tôi, và tôi đã cố gắng kết hợp khác nhau của "git show", "git apply", "patch", v.v.


232
2018-03-03 20:28



Điều này đã lưu ngày của tôi sau khi nhập nhầm git stash drop thay vì git stash pop. - Jörn Horstmann
Tốt đẹp. Tôi chỉ nhận ra rằng nó in ra SHA1 khi nó rơi xuống một dấu gạch ngang (tức là, khi bạn bật). - asmeurer
Hãy lưu ý rằng điều này áp dụng (duh!) Stash cho cây làm việc hiện tại. Nếu cây bị bẩn, bạn có thể muốn sử dụng nhánh tạm thời hoặc stash trước, áp dụng dấu gạch chéo từ SHA-1, stash một lần nữa và sau đó bật thứ hai để stash cuối cùng (được gọi là stash @ {1}). - musiKk


Tôi vừa mới xây dựng một lệnh giúp tôi tìm được chỗ bị mất của mình:

for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

Điều này liệt kê tất cả các đối tượng trong cây .git / objects, định vị các đối tượng có kiểu commit, sau đó hiển thị một bản tóm tắt của từng đối tượng. Từ thời điểm này nó chỉ là một vấn đề của việc tìm kiếm thông qua các cam kết để tìm một WIP thích hợp về công việc: 6a9bb2 "(" công việc "là chi nhánh của tôi, 619bb2 là một cam kết gần đây).

Tôi lưu ý rằng nếu tôi sử dụng "git stash apply" thay vì "git stash pop" thì tôi sẽ không gặp vấn đề này, và nếu tôi sử dụng "git stash save" thông điệp"thì cam kết có thể dễ tìm hơn.

Cập nhật: Với ý tưởng của Nathan, điều này trở nên ngắn hơn:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less

68
2017-09-18 02:10



Cảm ơn vì đã mài giũa kỹ năng vỏ của tôi một lần nữa! Tôi đã có thể tìm thấy stash mất tích của tôi với vòng lặp của bạn và hiển thị git. - wprater
Điều này là hoàn hảo: hiển thị tóm tắt cho cam kết trong less đã làm cái này không đáng kể. - cbowns


Để có danh sách các dấu gạch ngang vẫn còn trong kho lưu trữ của bạn, nhưng không thể truy cập được nữa:

git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

Nếu bạn đã đặt tiêu đề cho stash của mình, hãy thay thế "WIP" trong -grep=WIP ở cuối lệnh bằng một phần thông điệp của bạn, ví dụ: -grep=Tesselation.

Lệnh này là grepping cho "WIP" vì thông điệp cam kết mặc định cho một stash là trong biểu mẫu WIP on mybranch: [previous-commit-hash] Message of the previous commit.


57
2018-05-04 06:42



Điều này làm việc tuyệt vời cho tôi - Nhưng tôi phải chỉnh sửa câu trả lời sao cho không gian đôi sau cut -d được hiển thị. Với một không gian duy nhất, cut không nhận ra dấu phân tách. - s4y
Tôi đã chỉnh sửa lại để tạo cut args thậm chí còn rõ ràng hơn - dty
echo 'git fsck --không thể truy cập được | grep cam kết | cắt -d "" -f3 | xargs git log --merges --no-walk --grep = WIP '> / usr / local / bin / git-stashlog; chmod a + rx / usr / local / bin / git-stashlog # git stashlog - Erik Martino
Hoặc bạn có thể thêm nó vào .gitconfig của bạn như một bí danh (trước lệnh với một !). - asmeurer
Cố gắng chấp nhận và các câu trả lời pop và didnt làm việc. Điều này đã làm. - juliangonzalez


git fsck --unreachable | grep commit nên hiển thị sha1, mặc dù danh sách nó trả về có thể khá lớn. git show <sha1> sẽ hiển thị nếu đó là cam kết bạn muốn.

git cherry-pick -m 1 <sha1> sẽ hợp nhất cam kết vào nhánh hiện tại.


36
2017-09-18 02:08



Ah, điều đó chắc chắn cải thiện theo phương pháp của tôi! Tôi có 130 cam kết không thể truy cập ngay bây giờ vì vậy tôi vẫn cần nửa cuối của giải pháp của tôi. - Greg Hewgill


Nếu bạn muốn khôi phục lại một stash bị mất, bạn cần phải tìm hash của stash bị mất của bạn đầu tiên.

Như Aristotle Pagaltzis đã gợi ý git fsck sẽ giúp bạn.

Cá nhân tôi sử dụng log-all bí danh hiển thị cho tôi mọi cam kết (cam kết có thể phục hồi) để có cái nhìn tốt hơn về tình huống:

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)

Bạn có thể thực hiện tìm kiếm nhanh hơn nữa nếu bạn chỉ tìm kiếm thông báo "WIP on".

Một khi bạn biết sha1 của bạn, bạn chỉ cần thay đổi stash stash của bạn để thêm stash cũ:

git update-ref refs/stash ed6721d

Có thể bạn sẽ thích có một thông điệp liên kết để -m

git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d

Và thậm chí bạn sẽ muốn sử dụng tên này làm bí danh:

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1

23
2018-06-23 14:20



Tôi thích cái này vì nó cho thấy mọi thứ theo thứ tự lịch sử. - joeytwiddle
Tuy nhiên -d\\  nên là -d\  (hoặc thậm chí rõ ràng hơn -d' ') - joeytwiddle
Đã xảy ra lỗi: "gây tử vong: đối số mơ hồ" đang lúng túng ": bản sửa đổi hoặc đường dẫn không xác định không có trong cây đang hoạt động". - Daniel Ryan
bạn cũng cần phải bọc lệnh phụ với dấu ngoặc kép git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721 - Andrei Shostik