Kadang-kadang saya menjatuhkan DVD-rip ke proyek situs web, lalu dengan ceroboh git commit -a -m ...
, dan, zap, repo itu dibengkak oleh 2.2 pertunjukan. Lain kali saya melakukan beberapa pengeditan, menghapus file video, dan melakukan segalanya, tetapi file yang dikompresi masih ada di repositori, dalam sejarah.
Saya tahu saya bisa memulai cabang dari komit itu dan rebase satu cabang ke yang lain. Tapi apa yang harus saya lakukan untuk menggabungkan 2 komitmen sehingga file besar tidak muncul dalam sejarah dan dibersihkan dalam prosedur pengumpulan sampah?
git filter-branch
, tetapi saya menemukan yang sebaliknya benar.Jawaban:
Gunakan BFG Repo-Cleaner , alternatif yang lebih sederhana dan lebih cepat untuk
git-filter-branch
secara khusus dirancang untuk menghapus file yang tidak diinginkan dari riwayat Git.Ikuti petunjuk penggunaan dengan hati-hati , bagian intinya adalah ini:
File apa pun yang berukuran lebih dari 100MB (yang tidak ada dalam komit terbaru Anda ) akan dihapus dari riwayat repositori Git Anda. Anda kemudian dapat menggunakan
git gc
untuk membersihkan data yang mati:BFG biasanya setidaknya 10-50x lebih cepat daripada berjalan
git-filter-branch
, dan umumnya lebih mudah digunakan.Pengungkapan penuh: Saya penulis Repo-Cleaner BFG.
sumber
git push --force
setelah langkah Anda, jika repo jarak jauh masih tidak berubah.git push --force
. Juga patut dicatat: push paksa mungkin tidak diizinkan oleh remote (gitlab.com tidak, secara default. Harus "membuka proteksi" cabang).Apa yang ingin Anda lakukan sangat mengganggu jika Anda telah menerbitkan riwayat ke pengembang lain. Lihat “Memulihkan Dari Hulu Rebase” dalam
git rebase
dokumentasi untuk langkah-langkah yang diperlukan setelah memperbaiki riwayat Anda.Anda memiliki setidaknya dua opsi:
git filter-branch
dan rebase interaktif, keduanya dijelaskan di bawah ini.Menggunakan
git filter-branch
Saya memiliki masalah yang sama dengan data uji biner besar dari impor Subversion dan menulis tentang menghapus data dari repositori git .
Katakanlah sejarah git Anda adalah:
Perhatikan bahwa
git lola
ini adalah alias yang tidak standar tetapi sangat bermanfaat. Dengan--name-status
sakelar, kita dapat melihat modifikasi hierarki yang terkait dengan setiap komit.Dalam komit “Careless” (yang nama objek SHA1nya ce36c98) file tersebut
oops.iso
adalah DVD-rip yang ditambahkan secara tidak sengaja dan dihapus di komit berikutnya, cb14efd. Menggunakan teknik yang dijelaskan dalam posting blog tersebut, perintah untuk mengeksekusi adalah:Pilihan:
--prune-empty
menghapus komit yang menjadi kosong ( yaitu , jangan ubah susunan pohon) sebagai hasil dari operasi filter. Dalam kasus biasa, opsi ini menghasilkan riwayat yang lebih bersih.-d
memberi nama direktori sementara yang belum ada untuk digunakan untuk membangun riwayat yang difilter. Jika Anda menjalankan distribusi Linux modern, menspesifikasikan sebuah pohon/dev/shm
akan menghasilkan eksekusi yang lebih cepat .--index-filter
adalah acara utama dan berjalan melawan indeks pada setiap langkah dalam sejarah. Anda ingin menghapus dioops.iso
mana pun ditemukan, tetapi tidak ada di semua komit. Perintahgit rm --cached -f --ignore-unmatch oops.iso
menghapus DVD-rip ketika ada dan tidak gagal sebaliknya.--tag-name-filter
menjelaskan cara menulis ulang nama tag. Filtercat
adalah operasi identitas. Repositori Anda, seperti contoh di atas, mungkin tidak memiliki tag apa pun, tetapi saya menyertakan opsi ini untuk generalisasi penuh.--
menentukan akhir opsi untukgit filter-branch
--all
berikut--
ini adalah singkatan untuk semua referensi. Repositori Anda, seperti contoh di atas, mungkin hanya memiliki satu ref (master), tetapi saya menyertakan opsi ini untuk generalisasi penuh.Setelah beberapa berputar, sejarahnya sekarang:
Perhatikan bahwa komit “Careless” yang baru hanya menambahkan
other.html
dan bahwa komit “Remove DVD-rip” tidak lagi ada di cabang master. Cabang yang dilabelirefs/original/refs/heads/master
berisi komitmen asli Anda jika Anda melakukan kesalahan. Untuk menghapusnya, ikuti langkah-langkah di "Daftar Periksa untuk Mengecilkan Gudang."Untuk alternatif yang lebih sederhana, klon repositori untuk membuang bit yang tidak diinginkan.
Menggunakan
file:///...
klon URL akan menyalin objek daripada membuat hardlink saja.Sekarang sejarah Anda adalah:
Nama objek SHA1 untuk dua commit pertama ("Index" dan "Admin page") tetap sama karena operasi filter tidak mengubah commit tersebut. "Careless" hilang
oops.iso
dan "Halaman login" mendapat induk baru, jadi SHA1 mereka memang berubah.Rebase interaktif
Dengan riwayat:
Anda ingin menghapus
oops.iso
dari "Careless" seolah-olah Anda tidak pernah menambahkannya, dan kemudian "Remove DVD-rip" tidak berguna bagi Anda. Dengan demikian, rencana kami melakukan rebase interaktif adalah menjaga “halaman Admin,” sunting “Careless,” dan buang “Remove DVD-rip.”Menjalankan
$ git rebase -i 5af4522
memulai editor dengan konten berikut.Menjalankan rencana kami, kami memodifikasinya
Artinya, kita menghapus baris dengan "Hapus DVD-rip" dan mengubah operasi pada "Careless" menjadi
edit
daripadapick
.Simpan-keluar dari editor menjatuhkan kita pada prompt perintah dengan pesan berikut.
Seperti yang dikatakan pesan kepada kita, kita berada pada komit “Careless” yang ingin kita edit, jadi kita menjalankan dua perintah.
Yang pertama menghapus file yang menyinggung dari indeks. Yang kedua memodifikasi atau mengubah "Ceroboh" menjadi indeks yang diperbarui dan
-C HEAD
memerintahkan git untuk menggunakan kembali pesan komit lama. Akhirnya,git rebase --continue
lanjutkan dengan sisa operasi rebase.Ini memberikan sejarah:
yang kamu inginkan.
sumber
-f
(atau--force
) kegit push
perintah Anda : “Biasanya, perintah tersebut menolak untuk memperbarui referensi jarak jauh yang bukan merupakan nenek moyang dari referensi lokal yang digunakan untuk menimpanya. Bendera ini menonaktifkan centang. Ini dapat menyebabkan repositori jarak jauh kehilangan komit; gunakan dengan hati-hati. "... "git rm --cached -rf --ignore-unmatch path/to/dir"...
Mengapa tidak menggunakan perintah sederhana namun kuat ini?
The
--tree-filter
pilihan menjalankan perintah tertentu setelah setiap checkout proyek dan kemudian recommits hasil. Dalam hal ini, Anda menghapus file yang disebut DVD-rip dari setiap foto, apakah itu ada atau tidak.Jika Anda tahu komit mana yang memperkenalkan file besar (katakanlah 35dsa2), Anda dapat mengganti HEAD dengan 35dsa2 .. KEPALA untuk menghindari penulisan ulang terlalu banyak riwayat, sehingga menghindari komitmen yang berbeda jika Anda belum mendorong. Komentar ini dari @ alpha_989 tampaknya terlalu penting untuk ditinggalkan di sini.
Lihat tautan ini .
sumber
fatal: bad revision 'rm'
, yang saya perbaiki dengan menggunakan"
bukan'
. Perintah keseluruhan:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
commit
mana Anda meletakkan file itu (katakanlah35dsa2
), Anda dapat menggantinyaHEAD
dengan35dsa2..HEAD
.tree-filter
jauh lebih lambat daripadaindex-filter
cara itu tidak akan mencoba untuk checkout semua komit dan menulis ulang. jika Anda menggunakan KEPALA, itu akan mencoba melakukan itu.(Jawaban terbaik yang pernah saya lihat untuk masalah ini adalah: https://stackoverflow.com/a/42544963/714112 , disalin di sini karena utas ini muncul tinggi di peringkat pencarian Google tetapi yang lain tidak)
🚀 Shell satu-liner yang sangat cepat 🚀
Skrip shell ini menampilkan semua objek gumpalan di repositori, diurutkan dari yang terkecil hingga yang terbesar.
Untuk repo sampel saya, ini berjalan sekitar 100 kali lebih cepat daripada yang lain yang ditemukan di sini.
Pada sistem Athlon II X4 saya yang tepercaya , ia menangani repositori Kernel Linux dengan 5.622.155 objek hanya dalam satu menit .
Skrip Dasar
Ketika Anda menjalankan kode di atas, Anda akan mendapatkan output yang dapat dibaca manusia seperti ini:
🚀 Penghapusan File Cepat 🚀
Misalkan Anda ingin menghapus file
a
danb
dari setiap commit yang dapat dijangkauHEAD
, Anda dapat menggunakan perintah ini:sumber
--tag-name-filter cat
untuk menandai ulang komit yang sesuai saat ditulis ulang, yaitugit filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD
(lihat jawaban terkait ini )git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD
hak perintah kerja kelelawargit rev-list --objects --all \ | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \ | awk '/^blob/ {print substr($0,6)}' \ | sort --numeric-sort --key=2 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Setelah mencoba hampir setiap jawaban dalam SO, saya akhirnya menemukan permata ini yang dengan cepat menghapus dan menghapus file-file besar di repositori saya dan memungkinkan saya untuk melakukan sinkronisasi lagi: http://www.zyxware.com/articles/4027/how-to-delete -files-secara permanen-dari-repositori git-lokal-dan-jarak-jauh Anda
CD ke folder kerja lokal Anda dan jalankan perintah berikut:
ganti FOLDERNAME dengan file atau folder yang ingin Anda hapus dari repositori git yang diberikan.
Setelah ini selesai jalankan perintah berikut untuk membersihkan repositori lokal:
Sekarang dorong semua perubahan ke repositori jarak jauh:
Ini akan membersihkan repositori jarak jauh.
sumber
Perintah-perintah ini berfungsi dalam kasus saya:
Ini sedikit berbeda dari versi di atas.
Bagi mereka yang perlu mendorong ini ke github / bitbucket (Saya hanya menguji ini dengan bitbucket):
sumber
git rm --cached files
. Proposisi Greg Bacon lebih lengkap, dan hampir sama dengan tambang ini, tetapi ia melewatkan - memaksa indeks untuk kasus ketika Anda menggunakan cabang-filter untuk beberapa kali, dan ia menulis begitu banyak info, bahwa versi saya seperti resume. itu.-f
opsi tidak hanya di-rf
sini dangit rm --cached -rf --ignore-unmatch oops.iso
bukangit rm --cached -r --ignore-unmatch oops.iso
sebagai per @ lfender6445 di bawah iniPerhatikan bahwa perintah ini bisa sangat merusak. Jika lebih banyak orang mengerjakan repo, mereka semua harus menarik pohon baru. Tiga perintah tengah tidak perlu jika tujuan Anda BUKAN untuk mengurangi ukuran. Karena cabang filter membuat cadangan dari file yang dihapus dan itu bisa tinggal di sana untuk waktu yang lama.
sumber
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
alih-alih yang pertama dari kode Andagit filter-branch --tree-filter 'rm -f path/to/file' HEAD
bekerja dengan baik untuk saya, meskipun saya mengalami masalah yang sama seperti yang dijelaskan di sini , yang saya selesaikan dengan mengikuti saran ini .Buku pro-git memiliki seluruh bab tentang penulisan ulang riwayat - lihat bagian
filter-branch
/ Menghapus File dari Setiap Komit .sumber
Jika Anda tahu komit Anda baru saja alih-alih melewati seluruh pohon, lakukan hal berikut:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
sumber
Saya mengalami ini dengan akun bitbucket, di mana saya secara tidak sengaja menyimpan cadangan * .jpa ginormous di situs saya.
git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all
Relpace
MY-BIG-DIRECTORY
dengan folder yang dimaksud untuk sepenuhnya menulis ulang riwayat Anda ( termasuk tag ).sumber: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/
sumber
Ini akan menghapusnya dari riwayat Anda
sumber
Saya pada dasarnya melakukan apa yang ada di jawaban ini: https://stackoverflow.com/a/11032521/1286423
(untuk sejarah, saya akan salin-tempel di sini)
Itu tidak berhasil, karena saya suka mengubah nama dan banyak hal. Jadi beberapa file besar ada di folder yang telah diubah namanya, dan saya pikir gc tidak bisa menghapus referensi ke file-file itu karena referensi pada
tree
objek yang menunjuk ke file tersebut. Solusi utama saya untuk benar-benar membunuhnya adalah dengan:Repo saya
.git
berubah dari 32MB menjadi 388KB, bahkan cabang-filter pun tidak bisa dibersihkan.sumber
git filter-branch
adalah perintah yang sangat kuat yang dapat Anda gunakan untuk menghapus file besar dari sejarah commit. File akan tetap untuk sementara dan Git akan menghapusnya di pengumpulan sampah berikutnya. Di bawah ini adalah proses lengkap dari menghapus file dari komit sejarah . Untuk keamanan, proses di bawah ini menjalankan perintah pada cabang baru terlebih dahulu. Jika hasilnya adalah apa yang Anda butuhkan, maka reset kembali ke cabang yang sebenarnya ingin Anda ubah.sumber
Gunakan Git Extensions , ini adalah alat UI. Ini memiliki plugin bernama "Temukan file besar" yang menemukan file lage di repositori dan memungkinkan menghapusnya dengan permen.
Jangan gunakan 'git filter-branch' sebelum menggunakan alat ini, karena ini tidak akan dapat menemukan file yang dihapus oleh 'filter-branch' (Altough 'filter-branch' tidak menghapus file sepenuhnya dari file paket repositori) .
sumber
Anda dapat melakukan ini menggunakan
branch filter
perintah:git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
sumber
Ada jawaban yang sangat bagus di utas ini, tetapi sementara itu banyak dari mereka sudah usang. Penggunaan
git-filter-branch
tidak lagi direkomendasikan, karena sulit digunakan dan sangat lambat pada repositori besar.git-filter-repo
jauh lebih cepat dan mudah digunakan.git-filter-repo
adalah skrip Python, tersedia di github: https://github.com/newren/git-filter-repo .Anda hanya perlu satu file: skrip Python3 git-filter-repo. Salin ke jalur yang termasuk dalam variabel PATH. Pada Windows Anda mungkin harus mengubah baris pertama skrip (lihat INSTALL.md). Anda perlu menginstal Python3 terinstal di sistem Anda, tetapi ini bukan masalah besar.
Pertama kamu bisa lari
Ini membantu Anda menentukan apa yang harus dilakukan selanjutnya.
Anda dapat menghapus file DVD-rip Anda di mana saja:
Filter-repo sangat cepat. Tugas yang memakan waktu sekitar 9 jam di komputer saya dengan filter-branch, diselesaikan dalam 4 menit dengan filter-repo. Anda dapat melakukan lebih banyak hal menyenangkan dengan filter-repo. Lihat dokumentasi untuk itu.
Peringatan: Lakukan ini di salinan repositori Anda. Banyak tindakan filter-repo tidak dapat diurungkan. filter-repo akan mengubah hash komit dari semua commit yang dimodifikasi (tentu saja) dan semua turunannya ke commit terakhir!
sumber
Ketika Anda mengalami masalah ini,
git rm
tidak akan cukup, karena git ingat bahwa file tersebut pernah ada dalam sejarah kami, dan dengan demikian akan menyimpan referensi untuk itu.Untuk memperburuk keadaan, rebasing juga tidak mudah, karena setiap referensi ke gumpalan akan mencegah kolektor sampah membersihkan ruang. Ini termasuk referensi jarak jauh dan referensi reflog.
Saya mengumpulkan
git forget-blob
, sebuah skrip kecil yang mencoba menghapus semua referensi ini, dan kemudian menggunakan cabang-filter git untuk menulis ulang setiap komit di cabang.Setelah gumpalan Anda benar-benar tidak direferensikan,
git gc
akan menyingkirkannyaPenggunaannya cukup sederhana
git forget-blob file-to-forget
. Anda bisa mendapatkan info lebih lanjut di sinihttps://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/
Saya menyatukan ini berkat jawaban dari Stack Overflow dan beberapa entri blog. Kredit untuk mereka!
sumber
Selain
git filter-branch
(solusi git lambat tapi murni) dan BFG (lebih mudah dan sangat performan), ada juga alat lain untuk memfilter dengan kinerja yang baik:https://github.com/xoofx/git-rocket-filter
Dari uraiannya:
Tujuan dari git-roket-filter mirip dengan perintah
git-filter-branch
sambil memberikan fitur unik berikut:sumber