Hapus folder dan isinya dari sejarah git / GitHub

318

Saya sedang mengerjakan repositori di akun GitHub saya dan ini adalah masalah yang saya temui.

  • Proyek Node.js dengan folder dengan beberapa paket npm diinstal
  • Paket-paket itu ada di node_modulesfolder
  • Menambahkan folder itu ke git repositori dan mendorong kode ke github (tidak memikirkan bagian npm pada waktu itu)
  • Menyadari bahwa Anda tidak benar-benar membutuhkan folder itu untuk menjadi bagian dari kode
  • Menghapus folder itu, mendorongnya

Pada saat itu, ukuran total repo git sekitar 6MB di mana kode aktual (semua kecuali folder itu) hanya sekitar 300 KB .

Sekarang apa yang saya cari pada akhirnya adalah cara untuk menghilangkan rincian folder paket itu dari sejarah git jadi jika seseorang mengkloningnya, mereka tidak perlu mengunduh sejarah senilai 6MB di mana satu-satunya file aktual yang akan mereka dapatkan pada komit terakhir adalah 300KB.

Saya mencari solusi yang mungkin untuk ini dan mencoba 2 metode ini

Intisari sepertinya berfungsi di mana setelah menjalankan skrip, itu menunjukkan bahwa ia menyingkirkan folder itu dan setelah itu menunjukkan bahwa 50 komit yang berbeda dimodifikasi. Tapi itu tidak membiarkan saya mendorong kode itu. Ketika saya mencoba untuk mendorongnya, katanya Branch up to datetetapi menunjukkan 50 komit diubah pada a git status. 2 metode lainnya juga tidak membantu.

Sekarang meskipun itu menunjukkan bahwa itu menghilangkan sejarah folder itu, ketika saya memeriksa ukuran repo itu di localhost saya, itu masih sekitar 6MB. (Saya juga menghapus refs/originalfolder tetapi tidak melihat perubahan dalam ukuran repo).

Apa yang ingin saya klarifikasi adalah, jika ada cara untuk menyingkirkan tidak hanya komit histori (yang merupakan satu-satunya hal yang saya pikir terjadi) tetapi juga file-file git tetap dengan asumsi seseorang ingin mengembalikan.

Katakanlah solusi yang disajikan untuk ini dan diterapkan di localhost saya tetapi tidak dapat direproduksi ke repo GitHub, apakah mungkin untuk mengkloning repo itu, kembalikan ke komit pertama melakukan trik dan mendorongnya (atau apakah itu berarti bahwa git akan masih memiliki riwayat semua komit itu - alias 6MB).

Tujuan akhir saya di sini adalah untuk pada dasarnya menemukan cara terbaik untuk menyingkirkan isi folder dari git sehingga pengguna tidak harus mengunduh barang senilai 6MB dan masih mungkin memiliki komitmen lain yang tidak pernah menyentuh folder modul (itu cukup kebanyakan dari mereka) dalam sejarah git.

Bagaimana saya bisa melakukan ini?

Kartik
sumber
3
Jika ada jawaban di bawah ini yang menyelesaikan masalah Anda, mungkin Anda harus mempertimbangkan menerimanya sebagai jawaban untuk pertanyaan Anda. meta.stackexchange.com/questions/5234/…
starbeamrainbowlabs
Jawaban terbaik adalah: stackoverflow.com/a/32886427/5973334
Kuzeko

Jawaban:

556

Jika Anda di sini untuk menyalin-menempelkan kode:

Ini adalah contoh yang menghapus node_modulesdari riwayat

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Apa yang sebenarnya dilakukan git:

Baris pertama beralih melalui semua referensi pada pohon yang sama ( --tree-filter) seperti KEPALA (cabang Anda saat ini), menjalankan perintah rm -rf node_modules. Perintah ini menghapus folder node_modules ( -r, tanpa -r, rmtidak akan menghapus folder), tanpa konfirmasi yang diberikan kepada pengguna ( -f). Yang ditambahkan --prune-emptymenghapus tidak berguna (tidak mengubah apa pun) melakukan secara rekursif.

Baris kedua menghapus referensi ke cabang lama itu.

Sisa perintah relatif mudah.

Mohsen
sumber
3
Hanya catatan tambahan: Saya dulu git count-objects -vmemeriksa apakah file-file itu benar-benar dihapus tetapi ukuran repositori tetap sama sampai saya mengkloning repositori lagi. Git mempertahankan salinan semua file asli yang saya pikir.
Davide Icardi
4
Dengan git non-kuno, ini mungkin harus dibaca --force-with-lease, bukan --force.
Griwes
4
Tidak satu pun dari perintah ini bekerja di windows. Atau setidaknya bukan Windows 10, silakan posting OS di mana "cut and paste" berfungsi
David
3
Untuk pengguna Windows 10, ini bekerja dengan baik di bawah Bash untuk Windows (saya menggunakan Ubuntu)
Andrej Kyselica
3
Saya mencobanya dengan shell windows dan dengan git bash, dan tidak berhasil. Perintah pertama berlalu, perintah kedua gagal!
Mohy Eldeen
240

Saya menemukan bahwa --tree-filteropsi yang digunakan dalam jawaban lain bisa sangat lambat, terutama pada repositori yang lebih besar dengan banyak komitmen.

Inilah metode yang saya gunakan untuk menghapus direktori sepenuhnya dari sejarah git menggunakan --index-filteropsi, yang berjalan lebih cepat:

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

Anda dapat memeriksa ukuran repositori sebelum dan sesudah gcdengan:

git count-objects -vH
Lee Netherton
sumber
3
dapatkah Anda menjelaskan mengapa ini jauh lebih cepat?
knocte
7
@knocte: from the docs ( git-scm.com/docs/git-filter-branch ). "--index-filter: ... mirip dengan filter pohon tetapi tidak memeriksa pohonnya, yang membuatnya lebih cepat"
Lee Netherton
23
Mengapa ini bukan jawaban yang diterima? Ini sangat teliti.
Fisikawan Gila
2
Jika melakukan ini di Windows, Anda perlu tanda kutip ganda, bukan tanda kutip tunggal.
Kris Morness
12
Melewati --quietke git rmatas mempercepat saya menulis ulang setidaknya dengan faktor 4.
ctusch
46

Selain jawaban populer di atas, saya ingin menambahkan beberapa catatan untuk sistem Windows . Perintah

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • berfungsi sempurna tanpa modifikasi apa pun ! Karena itu, Anda tidak boleh menggunakan Remove-Item, delatau apa pun, bukan rm -rf.

  • Jika Anda perlu menentukan path ke file atau direktori, gunakan garis miring seperti./path/to/node_modules

peserta
sumber
Ini tidak akan berfungsi pada Windows jika direktori berisi a. (titik) dalam nama.
Corneliu Serediuc
4
Dan saya menemukan solusinya. Gunakan koma ganda terbalik untuk perintah rm seperti ini: "rm -rf node.modules".
Corneliu Serediuc
23

Metode terbaik dan paling akurat yang saya temukan adalah mengunduh file bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

Kemudian jalankan perintah:

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

Jika Anda ingin menghapus file, gunakan opsi hapus file:

java -jar bfg.jar --delete-files *.pyc
Kim T
sumber
1
sangat mudah :) jika Anda ingin membuat shure bahwa hanya folder tertentu yang dihapus, ini akan membantu: stackoverflow.com/questions/21142986/…
emjay
9

Tampaknya jawaban terbaru untuk ini adalah tidak menggunakan filter-branchsecara langsung (setidaknya git sendiri tidak merekomendasikannya lagi), dan menunda yang berfungsi ke alat eksternal. Secara khusus, git-filter-repo saat ini direkomendasikan. Penulis alat itu memberikan argumen tentang mengapa menggunakan filter-branchsecara langsung dapat menyebabkan masalah.

Sebagian besar skrip multi-baris di atas untuk dihapus dirdari riwayat dapat ditulis ulang sebagai:

git filter-repo --path dir --invert-paths

Alat ini lebih kuat dari itu, rupanya. Anda dapat menerapkan filter berdasarkan penulis, email, refname, dan lainnya ( halaman manual lengkap di sini ). Selain itu, cepat . Instalasi mudah - didistribusikan dalam berbagai format .

André Anjos
sumber
Alat yang bagus! Berfungsi dengan baik di Ubuntu 20.04, Anda bisa pip3 install git-filter-repokarena ini hanya stdlib dan tidak menginstal dependensi apa pun. Di Ubuntu 18 itu tidak kompatibel dengan versi git distro Error: need a version of git whose diff-tree command has the --combined-all-paths option, tetapi cukup mudah untuk menjalankannya didocker run -ti ubuntu:20.04
kubanczyk
7

Lengkap salin & tempel resep, cukup tambahkan perintah di komentar (untuk solusi salin-tempel), setelah mengujinya:

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Setelah ini, Anda dapat menghapus baris "node_modules /" dari .gitignore

jgbarah
sumber
Mengapa Anda kemudian menghapus node_modulesdari .gitignore? Sehingga mereka bisa secara tidak sengaja berkomitmen lagi ??
Adamski
1
Itu tidak bisa dihapus dari gitignore, itu ditambahkan ke gitignore. Pesan komit mengatakan "git history", bukan "gitignore" :)
Danny Tuppeny
tetapi komentar mengatakan bahwa Anda dapat menghapus node_modulesdari .gitignore.
zavr
7

Untuk pengguna Windows, harap dicatat untuk menggunakan "alih-alih ' Juga ditambahkan -funtuk memaksa perintah jika cadangan lain sudah ada.

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force
kcode
sumber
3

Saya menghapus folder bin dan obj dari proyek C # lama menggunakan git di windows. Hati-hati dengan

git filter-branch --tree-filter "rm -rf bin" --prune-empty HEAD

Itu menghancurkan integritas instalasi git dengan menghapus folder usr / bin di folder install git.

LordObi
sumber