Mendaftar dan menghapus komit Git yang tidak di bawah cabang (menjuntai?)

146

Saya punya repositori Git dengan banyak commit yang tidak di bawah cabang tertentu, saya bisa git showmelakukannya, tetapi ketika saya mencoba membuat daftar cabang yang mengandungnya, ia tidak melaporkan apa-apa.

Saya pikir ini adalah masalah komit / pohon yang menjuntai (sebagai akibat dari -D cabang), jadi saya memangkas repo, tetapi saya masih melihat perilaku yang sama setelah itu:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

Tidak ada output, tidak ada yang menggantung (kan?). Tetapi komit ada

$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...

dan itu tidak dapat dijangkau melalui cabang apa pun seperti

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

tidak memberikan hasil.

Apa sebenarnya keadaan komitmen itu? Bagaimana saya bisa membuat daftar semua komitmen dalam kondisi yang sama? Bagaimana saya bisa menghapus komit seperti itu?

Samer Buna
sumber

Jawaban:

75

Tidak ada output, tidak ada yang menggantung (kan?)

Perhatikan bahwa komit yang dirujuk dari reflog Anda dianggap dapat dijangkau.

Apa sebenarnya keadaan komitmen itu? Bagaimana saya bisa mendaftar semua komit dengan status yang sama

Lewati --no-reflogsuntuk meyakinkan git fsckuntuk menunjukkannya kepada Anda.

Bagaimana saya bisa menghapus komit seperti itu?

Setelah entri reflog Anda kedaluwarsa, benda-benda itu juga akan dibersihkan oleh git gc.

Kadaluwarsa diatur oleh gc.pruneexpire, gc.reflogexpiredan gc.reflogexpireunreachablepengaturan. Lih git help config.

Defaultnya semua cukup masuk akal.

Aristoteles Pagaltzis
sumber
2
jadi Anda pada dasarnya mengatakan bahwa reflows untuk komit menggantung akan dihapus setelah beberapa saat secara otomatis?
MoralCode
2
Pada dasarnya: ya - kecuali bahwa pertanyaannya agak membingungkan. Saya mengatakan bahwa semua entri reflog dihapus secara otomatis setelah beberapa saat, tetapi Anda dapat mengubahnya melalui pengaturan konfigurasi. Dan karena komit hanya disebut menggantung ketika tidak ada yang menunjuk padanya - termasuk entri reflog -, "reflog untuk menggantung komitmen" bukanlah sesuatu. Mereka akan menjadi "reflog untuk komitmen yang tidak terjangkau ".
Aristoteles Pagaltzis
'Mereka akan menjadi "reflog untuk komitmen yang tidak terjangkau".' Tetapi Anda mengatakan "komitmen yang dirujuk dari reflog Anda dianggap dapat dijangkau." Jadi bagaimana bisa "reflog untuk komitmen yang tidak terjangkau" menjadi sesuatu? Aku begitu bingung.
LarsH
1
Ya, saya tidak konsisten. Biasanya orang tidak memikirkan reflog, dan ketika mereka mengatakan "tidak dapat dijangkau" itu berarti "dari seorang ref". Bahkan git help glossarymendefinisikannya seperti itu ... sedangkan definisi untuk "dapat dijangkau" tidak dipersempit seperti itu, sehingga mereka bertentangan. Lucu - jadi apa yang saya katakan sebenarnya konsisten dengan kebingungan dalam gitglossary... Bukan konsep yang membingungkan, hanya terminologi. Intinya adalah bahwa "menggantung" komit adalah hal-hal yang tidak ada hubungannya. Apakah itu membantu jika saya mengatakan “reflogs untuk sebaliknya komit unreachable” ...?
Aristoteles Pagaltzis
Ini semua sangat membingungkan. Mari kita membuatnya sederhana. Ketika di cabang master, Anda melakukannya git commit, dan mendapatkan komit 000001. Maka Anda lakukan git commit --amend, yang memberi Anda komit 000002. Tidak ada tag atau cabang yang menunjuk 000001lagi, dan Anda tidak dapat melihatnya di log Anda tanpa --reflogopsi, tetapi jika Anda mau, Anda masih bisa mendapatkannya dengan git checkout 000001. Sekarang pertanyaannya adalah, Apakah 000001sebuah menjuntai komit, atau tidak terjangkau komit, atau tidak, atau keduanya?
chharvey
264

Untuk menghapus semua komitmen yang menjuntai dan yang dapat dijangkau dari reflog lakukan ini:

git reflog expire --expire-unreachable=now --all
git gc --prune=now

Tetapi yakinlah bahwa inilah yang Anda inginkan. Saya sarankan Anda membaca halaman manual tetapi inilah intinya:

git gcmenghapus objek yang tidak terjangkau (komit, pohon, gumpalan (file)). Objek tidak dapat dijangkau jika bukan bagian dari sejarah cabang tertentu. Sebenarnya ini sedikit lebih rumit:

git gc melakukan beberapa hal lain tetapi mereka tidak relevan di sini dan tidak berbahaya.

Objek yang tidak terjangkau yang lebih muda dari dua minggu tidak dihapus sehingga kami menggunakan --prune=nowyang berarti "menghapus objek yang tidak terjangkau yang dibuat sebelum sekarang".

Objek juga dapat dicapai melalui reflog. Sementara cabang mencatat sejarah beberapa proyek, reflog mencatat sejarah cabang-cabang ini. Jika Anda mengubah, setel ulang dll. Komit dihapus dari riwayat cabang tetapi git menyimpannya jika Anda menyadari bahwa Anda telah membuat kesalahan. Reflog adalah cara yang mudah untuk mengetahui operasi destruktif (dan lainnya) apa yang dilakukan pada cabang (atau KEPALA), sehingga memudahkan untuk membatalkan operasi yang merusak.

Jadi kita juga harus menghapus reflog untuk benar-benar menghapus semua yang tidak dapat dijangkau dari cabang. Kami melakukannya dengan berakhirnya --allreflog. Sekali lagi git menyimpan sedikit dari reflogs untuk melindungi pengguna jadi kami lagi harus mengatakan tidak untuk melakukannya: --expire-unreachable=now.

Karena saya terutama menggunakan reflog untuk memulihkan dari operasi destruktif yang biasanya saya gunakan --expire=now, yang mengisi reflog sepenuhnya.

tarsius
sumber
1
Saya memberi tahu Anda apa perintah untuk digunakan yang tidak jelas - tidakkah gc cukup? Jika Anda tidak pernah menggunakan git-reflog sebelum Anda tidak akan tahu. Jadi sekarang Anda tahu perintah apa yang harus Anda gunakan, Anda harus mencari opsi yang disebutkan di halaman manual mereka. Tentu saja saya malah bisa menyalin informasi itu dari sana ...
tarsius
1
Yah sebenarnya saya katakan persis apa yang dilakukannya: "hapus semua komitmen menjuntai dan yang dapat dijangkau dari reflogs". Jika Anda tidak tahu apa itu reflog: baca lagi manual.
tarsius
7
Meskipun jawaban yang diberikan mungkin benar, @ erikb85 benar dalam menunjukkan bahwa tidak ada pendidikan tentang apa yang diperintahkan kepada Anda. Menindaklanjuti dengan RTFM bahkan lebih tidak membantu. Ya, kita semua harus membaca semua dokumentasi. Dalam beberapa kasus mungkin orang yang melakukan pencarian tidak cukup membaca dokumentasi untuk mengetahui apa yang sedang terjadi. Jadi, sedikit pendidikan tentang apa yang dilakukan perintah akan sangat membantu bagi semua orang yang menemukan jawaban ini nanti.
Lee Saferite
@LeeSaferite harap Anda semua senang sekarang :-)
tarsius
12
git reflog expire --expire-unreachable=now --allmenjatuhkan semua simpanan Anda!
Vsevolod Golovanov
22

Saya memiliki masalah yang sama, masih setelah mengikuti semua saran di utas ini:

git reflog expire --expire-unreachable=now --all
git gc --prune=now
git fsck --unreachable --no-reflogs   # no output
git branch -a --contains <commit>     # no output
git show <commit>                     # still shows up

Jika bukan reflog dan bukan cabang, ... itu pasti sebuah tag !

git tag                             # showed several old tags created before the cleanup

Saya menghapus tag dengan git tag -d <tagname>dan membersihkan pembersihan, dan komitmen lama hilang.

jakub.g
sumber
Sudah ada jawaban tentang tag ( stackoverflow.com/a/37335660/450127 ), dan sepertinya ini tidak menambahkan sesuatu yang baru. Bukankah ini harus dihapus demi jawaban sebelumnya?
Ian Dunn
Memang, entah bagaimana saya mengabaikan jawaban itu. Meskipun 4 orang menemukan jawaban saya bermanfaat, jadi mungkin itu tidak berguna? Saya juga mengelompokkan semua kemungkinan menjadi satu jawaban singkat.
jakub.g
1
Meskipun digandakan, halaman ini dapat muncul di Hasil Google, dan segera membantu orang dengan masalah yang sama, lebih baik daripada hanya mengarahkan orang berulang-ulang ke tautan yang mungkin memiliki jawaban yang benar.
Alexandre T.
14
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

mungkin hanya perlu

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

untuk juga melaporkan cabang dari remote

lihat
sumber
terima kasih, sekarang saya menemukan remote / asal / berikutnya yang masih memegang komit ini. bagaimana cara menghapusnya? git push -d origin nexttidak membantu.
iRaS
terima kasih - git fetch --pruneberhasil. tetapi dalam semua jawaban, saya kehilangan tanda centang untuk tag yang mereferensikan komit ini. Saya masih tidak tahu cara memeriksa tag dengan komit (saya menghapus semua).
iRaS
Tetapi ... apakah ini berarti komit yang hanya dapat dijangkau dari cabang jarak jauh (dan tidak ada cabang lokal) dianggap dapat dijangkau, dan oleh karena git fsck --unreachableitu sebenarnya berkomunikasi melalui jaringan dengan remote untuk mengetahui komit mana yang dapat dijangkau?
LarsH
1
Menjawab pertanyaan saya sendiri ... ya, komitmen yang hanya dapat dijangkau dari cabang terpencil (dan tidak ada cabang lokal) dianggap dapat dijangkau; tetapi git fsck --unreachabletidak perlu berkomunikasi melalui jaringan dengan remote untuk mengetahui cabang-cabang mana yang berisi komit. Info cabang jarak jauh disimpan secara lokal, di bawah misalnya .git/refs/remotes/origin(atau di packed-refs).
LarsH
8

Saya punya masalah serupa. Saya berlari git branch --contains <commit>, dan tidak ada output seperti pada pertanyaan.

Tetapi bahkan setelah berlari

git reflog expire --expire-unreachable=now --all
git gc --prune=now

komit saya masih dapat diakses menggunakan git show <commit>. Ini karena salah satu komit di "cabangnya" yang terlepas / menjuntai telah ditandai. Saya menghapus tag, menjalankan perintah di atas lagi, dan saya emas. git show <commit>kembali fatal: bad object <commit>- persis apa yang saya butuhkan. Semoga ini bisa membantu orang lain yang sama macetnya dengan saya.

Andrew Larsson
sumber
bagaimana Anda menghapus tag?
bapors
@bapors Cantumkan semua tag, temukan tag yang merujuk komit yang dimaksud, lalu hapus. stackoverflow.com/questions/5480258/…
Andrew Larsson
4

Saya tidak sengaja mengenai situasi yang sama dan menemukan simpanan saya berisi referensi ke komit yang tidak dapat dijangkau, dan dengan demikian komit yang dianggap tidak dapat dijangkau itu dapat dicapai dari simpanan.

Inilah yang saya lakukan untuk membuatnya benar-benar tidak dapat dijangkau.

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now
Lei Zhao
sumber
2

git gc --prune=<date>default untuk memangkas objek yang lebih tua dari dua minggu lalu. Anda dapat menetapkan tanggal yang lebih baru. Tetapi, perintah git yang membuat objek longgar umumnya akan menjalankan git gc --auto (yang memangkas objek yang longgar jika jumlahnya melebihi nilai variabel konfigurasi gc.auto).

Apakah Anda yakin ingin menghapus komit ini? Pengaturan default gc.auto akan memastikan bahwa objek yang longgar tidak memakan jumlah memori yang tidak masuk akal, dan menyimpan objek yang longgar untuk jangka waktu tertentu umumnya merupakan ide yang bagus. Dengan begitu, jika Anda menyadari besok bahwa cabang Anda yang dihapus berisi komit yang Anda butuhkan, Anda dapat memulihkannya.

dublev
sumber