Bagaimana memulihkan simpanan yang dijatuhkan di Git?

1738

Saya sering menggunakan git stashdan git stash popuntuk menyimpan dan mengembalikan perubahan di pohon kerja saya. Kemarin saya memiliki beberapa perubahan di pohon kerja saya yang saya simpanan dan muncul, dan kemudian saya membuat lebih banyak perubahan pada pohon kerja saya. Saya ingin kembali dan meninjau perubahan simpanan kemarin, tetapi git stash poptampaknya menghapus semua referensi ke komit terkait.

Saya tahu bahwa jika saya gunakan git stashmaka .git / refs / stash berisi referensi dari commit yang digunakan untuk membuat simpanan. Dan .git / logs / refs / simpanan berisi seluruh simpanan. Tapi referensi itu hilang git stash pop. Saya tahu bahwa komit masih dalam repositori saya di suatu tempat, tetapi saya tidak tahu apa itu.

Apakah ada cara mudah untuk memulihkan referensi simpanan kemarin?

Perhatikan bahwa ini tidak penting bagi saya hari ini karena saya memiliki cadangan harian dan dapat kembali ke pohon kerja kemarin untuk mendapatkan perubahan saya. Saya bertanya karena pasti ada cara yang lebih mudah!

Greg Hewgill
sumber
74
Catatan untuk masa depan: Jika Anda tidak ingin kehilangan simpanan setiap kali melakukannya git stash pop, Anda dapat melakukannya git stash apply. Itu melakukan hal yang sama, kecuali itu tidak menghapus referensi ke simpanan yang diterapkan.
Kevin
3
Mencoba semuanya di sini, tidak dapat menemukan simpanan yang telah muncul. Senang sekali dengan jetbrains.com/help/idea/local-history.html
Juan Mendes
Saya punya masalah ini. Untuk memperbarui repo saya, saya berlari git stash, git pull -r upstream, git push -f origin, git stash pop, dan pop mengatakan "fatal: log untuk ref / simpanan kosong". 😲 Saya mencoba banyak jawaban ini, tidak ada yang berhasil. Ketika saya melihat .git / refs / simpanan , SHA ada di sana. Mungkin ada masalah dengan menandai drive jaringan Windows untuk sinkronisasi offline? 🤷‍♂️
brianary

Jawaban:

2786

Setelah Anda mengetahui hash dari simpanan yang Anda jatuhkan, Anda dapat menerapkannya sebagai simpanan:

git stash apply $stash_hash

Atau, Anda dapat membuat cabang terpisah untuknya

git branch recovered $stash_hash

Setelah itu, Anda dapat melakukan apa pun yang Anda inginkan dengan semua alat normal. Setelah selesai, cukup cabut rantingnya.

Menemukan hash

Jika Anda baru saja mengeluarkannya dan terminal masih terbuka, Anda masih memiliki nilai hash yang dicetak git stash poppada layar (terima kasih, Dolda).

Jika tidak, Anda dapat menemukannya menggunakan ini untuk Linux, Unix atau Git Bash untuk Windows:

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

... atau menggunakan Powershell untuk Windows:

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

Ini akan menunjukkan semua komit di ujung grafik komit Anda yang tidak lagi dirujuk dari cabang atau tag mana pun - setiap komit yang hilang, termasuk setiap komit simpanan yang pernah Anda buat, akan berada di suatu tempat di grafik itu.

Cara termudah untuk menemukan simpanan yang Anda inginkan mungkin dengan meneruskan daftar itu ke gitk:

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

... atau lihat jawabannya dari emragin jika menggunakan Powershell untuk Windows.

Ini akan meluncurkan browser repositori yang memperlihatkan kepada Anda setiap komitmen dalam repositori , terlepas dari apakah itu dapat dijangkau atau tidak.

Anda dapat menggantinya gitkdengan sesuatu seperti git log --graph --oneline --decoratejika Anda lebih suka grafik yang bagus di konsol daripada aplikasi GUI terpisah.

Untuk melihat komit simpanan, cari pesan komit dari formulir ini:

        WIP di somebranch : commithash Beberapa pesan komit lama

Catatan : Pesan komit hanya akan berada dalam formulir ini (dimulai dengan "WIP aktif") jika Anda tidak memberikan pesan saat Anda melakukannya git stash.

Aristoteles Pagaltzis
sumber
49
Jaydel mengeluarkan kata-kata itu dari mulutku. Posting ini menyelamatkan pekerjaan saya :) Saya hanya ingin menambahkan - mengingat tanggal Anda mengerjakan apa pun yang hilang membuat Anda lebih mudah menelusuri gitk untuk apa yang Anda cari.
Sridhar Sarnobat
4
@ Codey: Karena PowerShell. Saya tidak tahu apakah MsysGit mengirimkan biner AWK. Googling memberitahu saya bahwa sesuatu seperti %{ $_.Split(' ')[2]; }harus melakukan setara dengan {print $3}dalam awkperintah dalam PowerShell, tapi saya tidak memiliki sistem Windows untuk tes itu, dan Anda masih memerlukan setara untuk /dangling commit/bagian. Pokoknya, jalankan saja git fsck --no-reflogdan lihat outputnya. Anda ingin hash dari baris “dangling commit <commitID>”.
Aristoteles Pagaltzis
7
Perlu disebutkan bahwa pesan komit hanya akan memiliki string "WIP" jika Anda tidak memberikan pesan Anda sendiri ketika menyimpan (yaitu dengan melakukan git stash save "<message>").
Samir Aguiar
12
Jika Anda tahu kapan penurunan itu terjadi, Anda bisa menggunakan satu-liner ini untuk mendapatkan daftar komitmen menggantung dengan menambah waktu: git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sortentri terakhir mungkin adalah yang Anda inginkan stash apply.
ris8_allo_zen0
3
git stash apply {ref}memulihkan simpanan jatuh! gitsangat bagus itu harus ilegal!
Tom Russell
707

Jika Anda tidak menutup terminal, lihat saja output dari git stash popdan Anda akan memiliki ID objek dari simpanan jatuh. Biasanya terlihat seperti ini:

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

(Catatan yang git stash dropjuga menghasilkan baris yang sama.)

Untuk mendapatkan simpanan itu kembali, jalankan saja git branch tmp 2cae03e, dan Anda akan mendapatkannya sebagai cabang. Untuk mengonversikan ini menjadi simpanan, jalankan:

git stash apply tmp
git stash

Memiliki sebagai cabang juga memungkinkan Anda untuk memanipulasinya secara bebas; misalnya, untuk memilih-ceri atau menggabungkannya.

Dolda2000
sumber
54
Anda juga dapat melakukan git stash apply commitidkemudian git stashuntuk mendapatkan simpanan baru.
Matthew Flaschen
32
Perhatikan bahwa jika git secara otomatis menggabungkan simpanan dan memiliki konflik, itu tidak akan menunjukkan kepada Anda hash.
James
31
@ James: Kemudian lagi, jika konflik itu adalah hasil dari menjalankan git stash pop, itu tidak akan menjatuhkan simpanan juga, jadi itu biasanya tidak masalah.
Dolda2000
2
Tidak ada SHA dalam output pop git saya. :(
Akun Throw Away
2
@ Honey: Itulah intinya git stash pop. Jika Anda ingin menerapkan simpanan tanpa menjatuhkannya, gunakan git stash applysaja. Selain itu, jika Anda ingin menerapkan perubahan ke beberapa cabang, Anda juga bisa memilih komit sebagai gantinya.
Dolda2000
271

Hanya ingin menyebutkan tambahan ini untuk solusi yang diterima. Tidak segera jelas bagi saya saat pertama kali saya mencoba metode ini (mungkin seharusnya), tetapi untuk menerapkan simpanan dari nilai hash, cukup gunakan "git stash apply":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Ketika saya masih baru di git, ini tidak jelas bagi saya, dan saya mencoba berbagai kombinasi "git show", "git apply", "patch", dll.

Menyeberang
sumber
3
Perhatikan bahwa ini berlaku (duh!) Simpanan ke pohon yang sedang bekerja. Jika pohonnya kotor, Anda mungkin ingin menggunakan cabang sementara atau simpanan terlebih dahulu, menerapkan simpanan dari SHA-1, simpanan lagi dan kemudian seret simpanan kedua hingga terakhir (disebut simpanan @ {1}).
musiKk
111

Untuk mendapatkan daftar simpanan yang masih dalam repositori Anda, tetapi tidak dapat dijangkau lagi:

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

Jika Anda memberi judul untuk simpanan Anda, ganti "WIP" di -grep=WIPbagian akhir perintah dengan bagian dari pesan Anda, mis -grep=Tesselation.

Perintah ini mengambil "WIP" karena pesan komit default untuk simpanan ada dalam formulir WIP on mybranch: [previous-commit-hash] Message of the previous commit.

Senthil A Kumar
sumber
1
echo 'git fsck --unreachable | grep commit | cut -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
Atau Anda dapat menambahkan ini ke .gitconfig Anda sebagai alias (mendahului perintah dengan a !).
asmeurer
Menyelamatkan daging saya - yah tidak benar-benar menyelamatkan saya pengkodean ulang hari kerja - dihargai - mengingat bahwa saya baru saja turun baru-baru ini saya hanya mengambil SHA atas dari output perintah Anda - kemudian .... git simpanan berlaku SHA ... seperti yang disebutkan dalam jawaban lain - many thx
danday74
75

Saya baru saja membangun sebuah perintah yang membantu saya menemukan simpanan simpanan yang hilang:

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

Ini mencantumkan semua objek di pohon .git / objek, mencari objek yang bertipe commit, lalu memperlihatkan ringkasan masing-masing. Dari titik ini itu hanya masalah melihat melalui komitmen untuk menemukan "WIP pada pekerjaan: 6a9bb2" yang sesuai (("pekerjaan" adalah cabang saya, 619bb2 adalah komitmen baru-baru ini).

Saya perhatikan bahwa jika saya menggunakan "git stash apply" daripada "git stash pop" saya tidak akan memiliki masalah ini, dan jika saya menggunakan "git stash save message " maka komit mungkin lebih mudah ditemukan.

Pembaruan: Dengan ide Nathan, ini menjadi lebih pendek:

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

git fsck --unreachable | grep commitharus menunjukkan sha1, meskipun daftar yang dikembalikan mungkin cukup besar. git show <sha1>akan ditampilkan jika itu adalah komit yang Anda inginkan.

git cherry-pick -m 1 <sha1> akan menggabungkan komit ke cabang saat ini.

Nathan Jones
sumber
37

Setara dengan Windows PowerShell menggunakan gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

Mungkin ada cara yang lebih efisien untuk melakukan ini dalam satu pipa, tetapi ini berhasil.

emragin
sumber
1
Saya sangat berterima kasih atas jawaban Anda
Виталий Шебаниц
32

Jika Anda ingin menyembunyikan simpanan yang hilang, Anda harus menemukan hash simpanan yang hilang terlebih dahulu.

Sebagai Aristoteles Pagaltzis menyarankan git fsckharus membantu Anda.

Secara pribadi saya menggunakan log-allalias saya yang menunjukkan setiap commit (recoverable commit) untuk memiliki pandangan yang lebih baik tentang situasi:

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

Anda dapat melakukan pencarian lebih cepat jika Anda hanya mencari pesan "WIP aktif".

Setelah Anda tahu sha1 Anda, Anda cukup mengubah reflog simpanan Anda untuk menambahkan simpanan lama:

git update-ref refs/stash ed6721d

Anda mungkin lebih suka memiliki pesan terkait jadi a -m

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

Dan Anda bahkan ingin menggunakan ini sebagai alias:

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1
Colin Hebert
sumber
2
Namun -d\\ seharusnya -d\ (atau bahkan lebih jelas -d' ')
joeytwiddle
Mendapat kesalahan: "fatal: argumen ambigu 'menggantung': revisi yang tidak diketahui atau jalur tidak di pohon yang bekerja."
Daniel Ryan
Anda juga perlu membungkus sub-perintah dengan tanda kutip git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721
Andrei Shostik
18

Saya menyukai pendekatan Aristoteles, tetapi tidak suka menggunakan GITK ... karena saya terbiasa menggunakan GIT dari baris perintah.

Sebagai gantinya, saya mengambil komit yang menggantung dan mengeluarkan kode ke file DIFF untuk ditinjau di editor kode saya.

git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff

Sekarang Anda dapat memuat file diff / txt yang dihasilkan (yang ada di folder rumah Anda) ke dalam editor txt Anda dan melihat kode aktual dan SHA yang dihasilkan.

Maka gunakan saja

git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Shaheen Ghiassy
sumber
17

Anda dapat membuat daftar semua komitmen yang tidak dapat dijangkau dengan menuliskan perintah ini di terminal -

git fsck --unreachable

Periksa hash komit yang tidak dapat dijangkau -

git show hash

Akhirnya berlaku jika Anda menemukan barang simpanan -

git stash apply hash
Vivek Kumar
sumber
15

Mengapa orang menanyakan pertanyaan ini? Karena mereka belum tahu atau mengerti reflog.

Sebagian besar jawaban untuk pertanyaan ini memberikan perintah panjang dengan opsi yang hampir tidak akan diingat siapa pun. Jadi orang-orang masuk ke pertanyaan ini dan menyalin apa pun yang mereka pikir mereka butuhkan dan segera melupakannya setelah itu.

Saya akan menyarankan semua orang dengan pertanyaan ini untuk hanya memeriksa reflog (git reflog), tidak lebih dari itu. Setelah Anda melihat daftar semua komitmen itu, ada seratus cara untuk mengetahui komitmen apa yang Anda cari dan memilihnya atau membuat cabang darinya. Dalam prosesnya, Anda akan belajar tentang reflog dan opsi yang berguna untuk berbagai perintah dasar git.

RobbyD
sumber
1
Hai Robby. Ini relevan jika Anda sedang bekerja, dilacak, dan perlu mengambil kembali tempat Anda berhenti beberapa minggu yang lalu hanya untuk mengetahui Anda tidak dapat menemukan pekerjaan simpanan Anda - mungkin tersesat di suatu tempat dalam hal-hal lain yang Anda lakukan. reflog sangat bagus jika ini adalah sejarah baru-baru ini, tetapi tidak untuk kesenjangan waktu yang lama.
emragins
1
Hei emragins, saya setuju, tapi ini persis kasus penggunaan OP. Saya tidak tahu pasti bagaimana perintah lain yang diposting di sini akan berperilaku, tetapi geuss saya akan bahwa mereka juga akan berhenti bekerja setelah wasit ke komit simpanannya dibersihkan.
RobbyD
1
Hmm ... skenario di atas adalah yang membawa saya ke pertanyaan ini, dan saya tahu itu setidaknya beberapa minggu jika bahkan tidak lebih dekat ke satu bulan antara ketika saya (tanpa sadar) kehilangan simpanan saya dan ketika saya dapat memulihkannya.
emragins
15

Di OSX dengan git v2.6.4, saya hanya menjalankan git stash drop secara tidak sengaja, kemudian saya menemukannya dengan menelusuri langkah-langkah di bawah ini

Jika Anda tahu nama simpanan maka gunakan:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

jika tidak, Anda akan menemukan ID dari hasil secara manual dengan:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Kemudian ketika Anda menemukan commit-id tekan saja simpanan git berlaku {commit-id}

Semoga ini bisa membantu seseorang dengan cepat

Bisa tecim
sumber
12

Saya ingin menambahkan solusi yang diterima cara lain yang baik untuk melalui semua perubahan, ketika Anda tidak memiliki gitk tersedia atau tidak ada X untuk output.

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

for h in `cat tmp_commits`; do git show $h | less; done

Kemudian Anda mendapatkan semua diff untuk hash yang ditampilkan satu demi satu. Tekan 'q' untuk mendapatkan perbedaan berikutnya.

Phil
sumber
12

Saya tidak bisa mendapatkan jawaban untuk bekerja di Windows dalam jendela perintah sederhana (Windows 7 dalam kasus saya). awk, grepdan Select-stringtidak dikenali sebagai perintah. Jadi saya mencoba pendekatan yang berbeda:

  • Lari pertama: git fsck --unreachable | findstr "commit"
  • salin output ke notepad
  • temukan ganti "komit tidak dapat dijangkau" dengan start cmd /k git show

akan terlihat seperti ini:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • simpan sebagai file .bat dan jalankan
  • skrip akan membuka banyak jendela perintah, menunjukkan setiap komit
  • jika Anda menemukan yang Anda cari, jalankan: git stash apply (your hash)

mungkin bukan solusi terbaik, tetapi berhasil untuk saya

Koen
sumber
Anda dapat menggunakan git bash bahkan di Windows. Di git bash, Anda memiliki semua alat baris perintah (unixoid) yang Anda butuhkan.
Adrian W
10

Jawaban yang diterima oleh Aristoteles akan menunjukkan semua komitmen yang dapat dicapai, termasuk komitmen yang tidak bersifat simpanan. Untuk menyaring suara:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3

Ini hanya akan menyertakan komit yang memiliki persis 3 induk komit (yang akan dimiliki simpanan), dan yang pesannya menyertakan "WIP aktif".

Perlu diingat, bahwa jika Anda menyimpan simpanan Anda dengan pesan (misalnya git stash save "My newly created stash"), ini akan menggantikan pesan "WIP on ..." default.

Anda dapat menampilkan lebih banyak informasi tentang setiap komit, mis. Menampilkan pesan komit, atau meneruskannya ke git stash show:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3 | \
xargs -n1 -I '{}' bash -c "\
  git log -1 --format=medium --color=always '{}'; echo; \
  git stash show --color=always '{}'; echo; echo" | \
less -R
Brad Feehan
sumber
6

Favorit saya adalah one-liner ini:

git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )

Ini pada dasarnya ide yang sama dengan jawaban ini tetapi jauh lebih pendek. Tentu saja, Anda masih bisa menambahkan --graphuntuk mendapatkan tampilan seperti pohon.

Ketika Anda telah menemukan komit dalam daftar, lamar dengan

git stash apply THE_COMMIT_HASH_FOUND

Bagi saya, menggunakan --no-reflogsmemang mengungkapkan entri simpanan yang hilang, tetapi --unreachable(seperti yang ditemukan dalam banyak jawaban lain) tidak.

Jalankan pada git bash ketika Anda berada di bawah Windows.

Kredit: Detail dari perintah di atas diambil dari https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf

Adrian W
sumber
5

Memulihkannya dengan menggunakan langkah-langkah berikut:

  1. Identifikasi kode hash simpanan yang dihapus:

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

  2. Cherry Pick the Stash:

    git cherry-pick -m 1 $ stash_hash_code

  3. Selesaikan Konflik jika ada yang menggunakan:

    git mergetool

Selain itu Anda mungkin mengalami masalah dengan pesan komit jika Anda menggunakan gerrit. Harap simpan perubahan Anda sebelum mengikuti alternatif berikut:

  1. Gunakan hard reset ke komit sebelumnya dan kemudian komit perubahan ini.
  2. Anda juga dapat menyimpan perubahan, rebase, dan komitmen ulang.
Abhijeet
sumber
@ miva2 hasil edit Anda menghapus tautan ke jawaban yang paling benar dalam pertanyaan ini. Menambahkan tautan kembali ke komentar stackoverflow.com/questions/89332/…
Abhijeet
4

Yang saya cari di sini adalah bagaimana mengembalikan simpanan, terlepas dari apa yang telah saya periksa. Secara khusus, saya telah menyembunyikan sesuatu, kemudian memeriksa versi yang lebih lama, lalu mengeluarkannya, tetapi simpanan itu adalah larangan pada titik waktu sebelumnya, jadi simpanan itu hilang; Saya tidak bisa begitu saja git stashmendorongnya kembali ke tumpukan. Ini bekerja untuk saya:

$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179)
$ git checkout 27f6bd8ba3c
$ git reset HEAD^    # Make the working tree differ from the parent.
$ git stash # Put the stash back in the stack.
Saved working directory and index state WIP on (no branch): c2be516 Some message.
HEAD is now at c2be516 Some message.
$ git checkout somethingOld # Now we are back where we were.

Dalam retrospeksi, saya seharusnya menggunakan git stash applytidak git stash pop. Saya sedang melakukan bisectdan memiliki patch kecil yang ingin saya terapkan di setiap bisectlangkah. Sekarang saya melakukan ini:

$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
Ben
sumber
apakah ini jawaban, atau kelanjutan dari pertanyaan?
Alex Brown
Sedikit dari keduanya. Saya menemukan halaman ini karena saya kehilangan simpanan dan mencoba untuk mendapatkannya kembali. Kasus penggunaan bagi saya adalah melakukan dua bagian di mana saya ingin menerapkan perubahan sebelum menguji pada setiap langkah. Saya belajar dengan cara yang sulit bahwa Anda tidak bisa meletuskan, menguji, menyimpan, membagi dua karena itu dapat meninggalkan komit yang berbeda pada simpanan, karenanya stash apply.
Ben