Urungkan git reset --hard dengan file tidak terikat di area pementasan

110

Saya mencoba memulihkan pekerjaan saya. Aku dengan bodohnya melakukannya git reset --hard, tapi sebelumnya aku hanya get add .melakukan dan tidak melakukannya git commit. Tolong bantu! Ini log saya:

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

Apakah mungkin untuk membatalkan git reset --harddalam situasi ini?

eistrati.dll
sumber
@MarkLongair pria yang luar biasa! Anda baru saja mendapatkan pekerjaan saya kembali! Saya menulis skrip Python untuk membuat file dari semua output! Saya akan menambahkan skrip sebagai jawaban
Boy
4
Bukan 'bodoh' .. tapi 'naif' ... karena saya baru saja melakukan YANG SAMA!
Rosdi Kasim
Masih bisa menjadi bodoh ;-)
Duncan McGregor
Berikut artikel bagus tentang cara membalikkan sebagian dari ini. Ini akan membutuhkan beberapa pekerjaan manual.
Jan Swart
@MarkLongair `` temukan .git / objek / -type f -printf '% TY-% Tm-% Td% TT% p \ n' | sortir `` berhasil untuk saya. tanggal juga muncul, mulai periksa gumpalan dari akhir.
ZAky

Jawaban:

175

Anda harus dapat memulihkan file apa pun yang Anda tambahkan ke indeks (misalnya, seperti dalam situasi Anda, dengan git add .) meskipun ini mungkin sedikit berhasil. Untuk menambahkan file ke indeks, git menambahkannya ke database objek, yang berarti file tersebut dapat dipulihkan selama pengumpulan sampah belum terjadi. Ada contoh bagaimana melakukan ini diberikan dalam jawaban Jakub Narębski di sini:

Namun, saya mencobanya pada repositori percobaan, dan ada beberapa masalah - --cachedseharusnya --cache, dan saya menemukan bahwa itu tidak benar-benar membuat .git/lost-founddirektori. Namun, langkah-langkah berikut berhasil untuk saya:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

Itu harus mengeluarkan semua objek dalam database objek yang tidak dapat dijangkau oleh referensi mana pun, dalam indeks, atau melalui reflog. Outputnya akan terlihat seperti ini:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... dan untuk setiap gumpalan tersebut, Anda dapat melakukan:

git show 907b308

Untuk mengeluarkan isi file.


Output terlalu banyak?

Update dalam menanggapi sehe 's komentar di bawah ini:

Jika Anda menemukan bahwa Anda memiliki banyak komit dan struktur yang terdaftar dalam output dari perintah itu, Anda mungkin ingin menghapus dari output objek apapun yang direferensikan dari komit yang tidak direferensikan. (Biasanya Anda dapat kembali ke komit ini melalui reflog - kami hanya tertarik pada objek yang telah ditambahkan ke indeks tetapi tidak pernah dapat ditemukan melalui komit.)

Pertama, simpan output dari perintah tersebut, dengan:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

Sekarang nama objek dari komit yang tidak dapat dijangkau itu dapat ditemukan dengan:

egrep commit all | cut -d ' ' -f 3

Jadi Anda bisa menemukan pohon dan objek yang telah ditambahkan ke indeks, tetapi tidak dikomit pada titik mana pun, dengan:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

Itu sangat mengurangi jumlah objek yang harus Anda pertimbangkan.


Pembaruan: Philip Oakley di bawah ini menyarankan cara lain untuk mengurangi jumlah objek yang perlu dipertimbangkan, yaitu dengan hanya mempertimbangkan file yang paling baru diubah di bawah .git/objects. Anda dapat menemukannya dengan:

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(Saya menemukan finddoa itu di sini .) Akhir dari daftar itu mungkin terlihat seperti:

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

Dalam hal ini Anda dapat melihat objek tersebut dengan:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(Perhatikan bahwa Anda harus menghapus /di ujung jalur untuk mendapatkan nama objek.)

Mark Longair
sumber
sebelum saya memposting jawaban saya, saya mempertimbangkan untuk memasukkan langkah-langkah ini. Namun, saat mencoba ini dengan salah satu repo lokal saya, saya mendapatkan ratusan yang tidak dapat dijangkau, dan saya tidak dapat menemukan pendekatan yang memadai untuk, katakanlah, mengurutkan mereka berdasarkan tanggal pengiriman. Nah, itu akan luar biasa
lihat
3
Bukankah mungkin untuk melihat stempel waktu objek untuk objek terbaru yang lebih lambat dari komit terakhir Anda? atau saya melewatkan sesuatu.
Philip Oakley
1
@Philip Oakley: terima kasih atas saran itu, saya telah menambahkan saran untuk menemukan objek yang paling baru diubah dalam database objek.
Mark Longair
2
Bung, itu luar biasa! Itu menghemat @ $$ saya sekarang. Dan saya belajar ilmu git. Lebih baik perhatikan apa yang Anda tambahkan ke indeks ...
bowsersenior
2
untuk tidak mencuri lampu kapur siapa pun di sini. Tapi hanya sebagai catatan karena saya baru saja mengalami masalah yang sama dan mengira saya kehilangan semua pekerjaan saya. Bagi mereka yang menggunakan IDE , IDE biasanya memiliki solusi pemulihan satu klik. Dalam kasus PHPstorm saya hanya perlu mengklik kanan direktori dan pilih tampilkan riwayat lokal dan kemudian kembali ke status valid terakhir.
Shalom Sam
58

Saya baru saja melakukan git reset --harddan kehilangan satu komit. Tapi saya tahu hash komit, jadi saya bisa melakukannya git cherry-pick COMMIT_HASHuntuk memulihkannya.

Saya melakukan ini dalam beberapa menit setelah kehilangan commit, jadi mungkin berhasil untuk sebagian dari Anda.

Richard Saunders
sumber
5
Terima kasih terima kasih terima kasih Berhasil memulihkan hari kerja berkat jawaban ini
Dace
21
Mungkin perlu disebutkan, Anda dapat melihat hash tersebut menggunakan git reflog, misalnya git reset --hard-> git reflog( lihat hash HEAD @ {1}) dan terakhirgit cherry-pick COMMIT_HASH
Javier López
2
Teman-teman yang membaca ini, waspadalah bahwa ini hanya berfungsi untuk satu commit hash, jika ada lebih dari satu commit, itu tidak akan menyelesaikan masalah sekaligus!
Ain Tohvri
4
Anda benar-benar dapat menyetel ulang "maju". Jadi, jika Anda telah menyetel ulang melewati beberapa komit, melakukan 'git reset --hard <commit>' lainnya akan memulihkan seluruh rantai komit hingga titik itu. (mengingat bahwa mereka belum bergabung)
akimsko
6
Satu baris untuk memulihkan komit yang hilang git reset --hard( dengan asumsi Anda belum melakukan apa pun setelah perintah itu) adalahgit reset --hard @{1}
Ajedi32
13

Berkat Mark Longair, saya mendapatkan barang-barang saya kembali!

Pertama saya menyimpan semua hash ke dalam file:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

selanjutnya saya meletakkan semuanya (menghapus hal 'gumpalan yang tidak dapat dijangkau') dalam daftar dan memasukkan semua data ke dalam file baru ... Anda harus memilih file Anda dan mengganti namanya lagi yang Anda butuhkan ... tetapi saya hanya membutuhkan beberapa file..harap ini membantu seseorang ...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1
Nak
sumber
1
Iya! Inilah yang saya butuhkan. Saya suka skrip Python untuk membuat ulang semua file juga. Jawaban lain membuat saya gugup kehilangan data saya dengan pengumpulan sampah, jadi membuang file adalah kemenangan bagi saya :)
Eric Olson
1
Saya menulis skrip ini berdasarkan jawaban ini. Bekerja di luar kotak: github.com/pendashteh/git-recover-index
Alexar
1
Saya merasa lebih mudah untuk mengotomatisasi seperti ini: mkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh. File akan berakhir dilost/*.blob
Matija Nalis
6

Solusi @ Ajedi32 di komentar bekerja untuk saya dalam situasi ini.

git reset --hard @{1}

Perhatikan bahwa semua solusi ini bergantung pada tidak adanya git gc, dan beberapa di antaranya mungkin menyebabkannya, jadi saya akan mengeposkan konten direktori .git Anda sebelum mencoba apa pun sehingga Anda memiliki snapshot untuk kembali ke jika tidak ada. tidak bekerja untukmu.

Duncan McGregor
sumber
Saya memiliki sejumlah file yang tidak terikat. Kemudian saya melakukan 1 file, dan melakukan a git reset --hard, yang mengacaukan repo saya dengan cara yang tidak diketahui. @Duncan, apa yang dilakukan @ {1}? dan komentar mana yang Anda maksud? Apakah ini menyetel ulang git reset?
alpha_989
Saya pikir @ {1} adalah referensi relatif ke yang terakhir tetapi yang mengikat, tapi saya bukan ahli, hanya melaporkan apa yang berhasil untuk saya
Duncan McGregor
3

Mengalami masalah yang sama, tetapi belum menambahkan perubahan ke indeks. Jadi semua perintah di atas tidak mengembalikan perubahan yang saya inginkan.

Setelah semua jawaban rumit di atas, ini adalah petunjuk yang naif, tetapi mungkin itu akan menyelamatkan seseorang yang tidak memikirkannya terlebih dahulu, seperti yang saya lakukan.

Dengan putus asa, saya mencoba menekan CTRL-Z di editor saya (LightTable), sekali di setiap tab yang terbuka - untungnya ini memulihkan file di tab itu, ke status terbaru sebelum git reset --hard. HTH.

olange
sumber
1
Situasi yang persis sama, ketinggalan untuk menambahkan indeks dan melakukan hard reset dan tidak ada solusi yang berfungsi di atas. Yang ini adalah langkah SMART, terima kasih saya melakukan Ctrl + Z dan menyelamatkan hari saya. Editor saya: SublimeText. Satu dari sisiku!
Vivek
1

Ini mungkin jelas bagi para profesional git di luar sana, tetapi saya ingin memasangnya karena dalam pencarian saya yang panik, saya tidak melihat hal ini diangkat.

Saya membuat beberapa file, dan melakukan git reset --hard, sedikit panik, dan kemudian menyadari bahwa status saya menunjukkan semua file saya masih dalam stage serta semua penghapusannya tidak diatur.

Pada titik ini Anda dapat melakukan perubahan bertahap tersebut, selama Anda tidak melakukan tahap penghapusannya. Setelah ini, Anda hanya perlu mengerahkan keberanian untuk melakukannya git reset --hardsekali lagi, yang akan membawa Anda kembali ke perubahan yang telah Anda lakukan dan sekarang baru saja dilakukan.

Sekali lagi, ini mungkin bukan hal baru bagi kebanyakan orang, tetapi saya berharap karena ini membantu saya dan saya tidak menemukan apa pun yang menyarankan hal ini, ini dapat membantu orang lain.

ddoty
sumber
1

Ya ampun, saya menarik rambut saya sampai saya menemukan pertanyaan ini dan jawabannya. Saya percaya jawaban yang benar dan ringkas untuk pertanyaan yang diajukan hanya tersedia jika Anda menggabungkan dua komentar di atas jadi ini semua ada di satu tempat:

  1. Seperti yang disebutkan oleh chilicuil, jalankan git refloguntuk mengidentifikasi di sana hash komit yang ingin Anda buka lagi

  2. Seperti yang disebutkan oleh akimsko, Anda mungkin TIDAK ingin memilih ceri kecuali Anda hanya kehilangan satu komit, jadi Anda harus lari git reset --hard <hash-commit-you-want>

Catatan untuk pengguna egit Eclipse: Saya tidak dapat menemukan cara untuk melakukan langkah-langkah ini dalam Eclipse dengan egit. Menutup Eclipse, menjalankan perintah di atas dari jendela terminal, dan kemudian membuka kembali Eclipse bekerja dengan baik untuk saya.

Lolo
sumber
0

Solusi di atas dapat berfungsi, namun, ada cara yang lebih sederhana untuk memulihkannya daripada melalui gitpembatalan kompleks -s. Saya akan menebak bahwa kebanyakan git-reset terjadi pada sejumlah kecil file, dan jika Anda sudah menggunakan VIM, ini mungkin solusi yang paling hemat waktu. Peringatannya adalah Anda sudah harus menggunakan ViM'spersistent-undo, yang harus Anda gunakan dengan cara apa pun, karena ini memberi Anda kemampuan untuk membatalkan perubahan dalam jumlah yang tidak terbatas.

Berikut langkah-langkahnya:

  1. Di vim tekan :dan ketik perintah set undodir. Jika Anda telah persistent undomengaktifkan di Anda .vimrc, itu akan menunjukkan hasil yang mirip dengan undodir=~/.vim_runtime/temp_dirs/undodir.

  2. Di repo Anda gunakan git loguntuk mengetahui tanggal / waktu terakhir Anda membuat komit terakhir

  3. Di shell Anda, navigasikan ke undodirpenggunaan Anda cd ~/.vim_runtime/temp_dirs/undodir.

  4. Dalam direktori ini gunakan perintah ini untuk menemukan semua file yang telah Anda ubah sejak komit terakhir

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    Di sini "2018-03-20 11:24:44" adalah tanggal dan waktu pengubahan terakhir. Jika tanggal Anda melakukan git reset --hardadalah "2018-03-22", gunakan "2018-03-22", lalu gunakan "2018-03-23". Ini karena quirk of find, di mana batas bawah inklusif, dan batas atasnya eksklusif. https://unix.stackexchange.com/a/70404/242983

  5. Selanjutnya pergi ke masing-masing file yang membukanya di vim, dan lakukan "20m sebelumnya". Anda dapat menemukan detail lebih lanjut tentang "sebelumnya" dengan menggunakan "h sebelumnya". Di sini earlier 20mberarti kembali ke status file 20 menit ke belakang, dengan asumsi Anda melakukan git hard --reset, 20 menit mundur. Ulangi ini di semua file yang dikeluarkan dari findperintah. Saya yakin seseorang dapat menulis skrip yang akan menggabungkan hal-hal ini.

alpha_989
sumber
0

Saya menggunakan IntelliJ dan dapat menelusuri setiap file dan melakukan:

Edit -> reload from disk

Untungnya, saya baru saja melakukan yang git statusbenar sebelum menghapus perubahan kerja saya, jadi saya tahu persis apa yang harus saya muat ulang.

Forrest
sumber
0

Mengingat hierarki file Anda dan menggunakan teknik Mark Longair dengan modifikasi Phil Oakley menghasilkan hasil yang dramatis.

Pada dasarnya jika Anda setidaknya menambahkan file ke repo tetapi tidak melakukannya, Anda dapat memulihkan dengan menggunakan secara interaktif git show, memeriksa log, dan menggunakan pengalihan shell untuk membuat setiap file (mengingat jalur yang Anda minati).

HTH!

ainthu
sumber
0

Jika Anda baru saja meninjau, git diffmaka ada cara lain untuk memulihkan dari insiden seperti ini, bahkan ketika Anda belum melakukan perubahan: jika keluaran dari git diffmasih dalam buffer konsol Anda, Anda dapat menggulir ke atas, salin-tempel diff ke dalam file dan menggunakan patchalat untuk menerapkan diff untuk pohon Anda: patch -p0 < file. Pendekatan ini menyelamatkan saya beberapa kali.

Kebingungan
sumber