Bagaimana cara menghapus / menghapus file besar dari komit riwayat di repositori Git?

708

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?

culebrón
sumber
9
Artikel ini akan membantu Anda help.github.com/removing-sensitive-data
MBO
1
Perhatikan bahwa jika file besar Anda berada dalam subdir, Anda harus menentukan path relatif penuh.
Johan
1
Juga terkait help.github.com/en/articles/…
frederj
Banyak jawaban di bawah ini yang mengatakan BFG lebih mudah daripada itu git filter-branch, tetapi saya menemukan yang sebaliknya benar.
2540625

Jawaban:

605

Gunakan BFG Repo-Cleaner , alternatif yang lebih sederhana dan lebih cepat untuk git-filter-branchsecara khusus dirancang untuk menghapus file yang tidak diinginkan dari riwayat Git.

Ikuti petunjuk penggunaan dengan hati-hati , bagian intinya adalah ini:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

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 gcuntuk membersihkan data yang mati:

$ git gc --prune=now --aggressive

BFG biasanya setidaknya 10-50x lebih cepat daripada berjalan git-filter-branch, dan umumnya lebih mudah digunakan.

Pengungkapan penuh: Saya penulis Repo-Cleaner BFG.

Roberto Tyley
sumber
4
@tony Layak mengulangi seluruh prosedur kloning & kliring untuk melihat apakah pesan yang meminta Anda menarik kembali terjadi, tetapi hampir pasti karena server jauh Anda dikonfigurasikan untuk menolak pembaruan non-fast-forward (mis., dikonfigurasi untuk menghentikan Anda dari kehilangan sejarah - itulah yang ingin Anda lakukan). Anda perlu mengubah pengaturan pada remote, atau gagal, mendorong riwayat repo yang diperbarui ke repo kosong baru.
Roberto Tyley
1
@RobertoTyley Terima kasih. Saya sudah mencobanya 3 kali berbeda dan semuanya menghasilkan pesan yang sama. Jadi saya juga berpikir bahwa Anda benar tentang server jarak jauh yang sedang dikonfigurasikan untuk menolak pembaruan yang tidak terlalu cepat. Saya akan mempertimbangkan hanya mendorong repo yang diperbarui ke repo baru. Terima kasih!
Tony
7
@RobertoTyley Sempurna, Anda menghemat waktu saya, terima kasih banyak. By the way, mungkin harus dilakukan git push --forcesetelah langkah Anda, jika repo jarak jauh masih tidak berubah.
li2
3
+1 untuk ditambahkan git push --force. Juga patut dicatat: push paksa mungkin tidak diizinkan oleh remote (gitlab.com tidak, secara default. Harus "membuka proteksi" cabang).
MatrixManAtYrService
25
Saya pikir Trump jargon keluaran alat agak banyak.
Chris
564

Apa yang ingin Anda lakukan sangat mengganggu jika Anda telah menerbitkan riwayat ke pengembang lain. Lihat “Memulihkan Dari Hulu Rebase” dalam git rebasedokumentasi untuk langkah-langkah yang diperlukan setelah memperbaiki riwayat Anda.

Anda memiliki setidaknya dua opsi: git filter-branchdan 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:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Perhatikan bahwa git lolaini adalah alias yang tidak standar tetapi sangat bermanfaat. Dengan --name-statussakelar, kita dapat melihat modifikasi hierarki yang terkait dengan setiap komit.

Dalam komit “Careless” (yang nama objek SHA1nya ce36c98) file tersebut oops.isoadalah 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:

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

Pilihan:

  • --prune-emptymenghapus komit yang menjadi kosong ( yaitu , jangan ubah susunan pohon) sebagai hasil dari operasi filter. Dalam kasus biasa, opsi ini menghasilkan riwayat yang lebih bersih.
  • -dmemberi nama direktori sementara yang belum ada untuk digunakan untuk membangun riwayat yang difilter. Jika Anda menjalankan distribusi Linux modern, menspesifikasikan sebuah pohon /dev/shmakan menghasilkan eksekusi yang lebih cepat .
  • --index-filteradalah acara utama dan berjalan melawan indeks pada setiap langkah dalam sejarah. Anda ingin menghapus di oops.isomana pun ditemukan, tetapi tidak ada di semua komit. Perintah git rm --cached -f --ignore-unmatch oops.isomenghapus DVD-rip ketika ada dan tidak gagal sebaliknya.
  • --tag-name-filtermenjelaskan cara menulis ulang nama tag. Filter catadalah 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 untuk git filter-branch
  • --allberikut --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:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/  A   oops.iso
|   A   other.html
|
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Perhatikan bahwa komit “Careless” yang baru hanya menambahkan other.htmldan bahwa komit “Remove DVD-rip” tidak lagi ada di cabang master. Cabang yang dilabeli refs/original/refs/heads/masterberisi komitmen asli Anda jika Anda melakukan kesalahan. Untuk menghapusnya, ikuti langkah-langkah di "Daftar Periksa untuk Mengecilkan Gudang."

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

Untuk alternatif yang lebih sederhana, klon repositori untuk membuang bit yang tidak diinginkan.

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

Menggunakan file:///...klon URL akan menyalin objek daripada membuat hardlink saja.

Sekarang sejarah Anda adalah:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Nama objek SHA1 untuk dua commit pertama ("Index" dan "Admin page") tetap sama karena operasi filter tidak mengubah commit tersebut. "Careless" hilang oops.isodan "Halaman login" mendapat induk baru, jadi SHA1 mereka memang berubah.

Rebase interaktif

Dengan riwayat:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Anda ingin menghapus oops.isodari "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 5af4522memulai editor dengan konten berikut.

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Menjalankan rencana kami, kami memodifikasinya

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

Artinya, kita menghapus baris dengan "Hapus DVD-rip" dan mengubah operasi pada "Careless" menjadi editdaripada pick.

Simpan-keluar dari editor menjatuhkan kita pada prompt perintah dengan pesan berikut.

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

Seperti yang dikatakan pesan kepada kita, kita berada pada komit “Careless” yang ingin kita edit, jadi kita menjalankan dua perintah.

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

Yang pertama menghapus file yang menyinggung dari indeks. Yang kedua memodifikasi atau mengubah "Ceroboh" menjadi indeks yang diperbarui dan -C HEADmemerintahkan git untuk menggunakan kembali pesan komit lama. Akhirnya, git rebase --continuelanjutkan dengan sisa operasi rebase.

Ini memberikan sejarah:

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

yang kamu inginkan.

Greg Bacon
sumber
4
Mengapa saya tidak bisa mendorong ketika menggunakan cabang git filter, gagal mendorong beberapa referensi ke '[email protected]: product / myproject.git' Untuk mencegah Anda dari kehilangan riwayat, pembaruan yang tidak maju cepat ditolak Gabungkan remote berubah sebelum mendorong lagi.
Agung Prasetyo
11
Tambahkan opsi -f(atau --force) ke git pushperintah 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. "
Greg Bacon
5
Ini adalah jawaban yang sangat teliti menjelaskan penggunaan git-filter-branch untuk menghapus file besar yang tidak diinginkan dari sejarah, tetapi perlu dicatat bahwa sejak Greg menulis jawabannya, Repo-Cleaner BFG telah dirilis, yang seringkali lebih cepat dan lebih mudah untuk gunakan - lihat jawaban saya untuk detail.
Roberto Tyley
1
Setelah saya melakukan salah satu dari prosedur di atas, repositori jarak jauh (di GitHub) TIDAK menghapus file besar. Hanya orang lokal yang melakukannya. Saya memaksakan dorongan dan nada. Apa yang saya lewatkan?
azatar
1
ini juga berfungsi pada dirs. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
rynop
198

Mengapa tidak menggunakan perintah sederhana namun kuat ini?

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

The --tree-filterpilihan 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 .

Gary Gauh
sumber
3
Ini solusi yang bagus! Saya telah membuat intisari yang memiliki skrip python untuk membuat daftar file & git cmd yang akan menghapus file yang ingin Anda bersihkan gist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata
5
Jauh lebih baik daripada bfg. Saya tidak dapat membersihkan file dari git dengan bfg, tetapi perintah ini membantu
podarok
4
Ini bagus. Hanya catatan untuk orang lain bahwa Anda harus melakukan ini per cabang jika file besar di banyak cabang.
James
2
Pada Windows saya dapatkan 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
marcotama
2
Jika Anda tahu di commitmana Anda meletakkan file itu (katakanlah 35dsa2), Anda dapat menggantinya HEADdengan 35dsa2..HEAD. tree-filterjauh lebih lambat daripada index-filtercara itu tidak akan mencoba untuk checkout semua komit dan menulis ulang. jika Anda menggunakan KEPALA, itu akan mencoba melakukan itu.
alpha_989
86

(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

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

Ketika Anda menjalankan kode di atas, Anda akan mendapatkan output yang dapat dibaca manusia seperti ini:

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

🚀 Penghapusan File Cepat 🚀

Misalkan Anda ingin menghapus file adan bdari setiap commit yang dapat dijangkau HEAD, Anda dapat menggunakan perintah ini:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Sridhar Sarnobat
sumber
3
Jika repo Anda memiliki tag, Anda mungkin juga ingin menambahkan bendera --tag-name-filter catuntuk menandai ulang komit yang sesuai saat ditulis ulang, yaitu git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD(lihat jawaban terkait ini )
naitsirhc
3
Instruksi Mac dan beberapa info lainnya muncul di pos tertaut asli
nruth
3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADhak perintah kerja kelelawar
eleijonmarck
jawaban favorit saya sedikit perubahan untuk digunakan pada mac os (menggunakan perintah gnu)git 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
Florian Oswald
skrip keren dengan daftar rev tetapi tidak berfungsi untuk saya sebagai alias, ada ide bagaimana melakukan itu?
Robin Manoli
47

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:

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

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:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Sekarang dorong semua perubahan ke repositori jarak jauh:

git push --all --force

Ini akan membersihkan repositori jarak jauh.

Justin
sumber
Bekerja seperti pesona bagi saya.
Ramon Vasconcelos
3
Ini juga bekerja untuk saya. Menghapus folder tertentu (dalam kasus saya, yang berisi file terlalu besar atau repo Github) pada repositori, tetapi menyimpannya di sistem file lokal jika ada.
skizzo
Bekerja untukku! tidak ada sejarah yang tersisa yang berpotensi membingungkan (jika seseorang tempat untuk mengkloning sekarang), pastikan Anda memiliki rencana untuk memperbarui tautan yang rusak, dependensi, dll
ruoho ruotsi
38

Perintah-perintah ini berfungsi dalam kasus saya:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Ini sedikit berbeda dari versi di atas.

Bagi mereka yang perlu mendorong ini ke github / bitbucket (Saya hanya menguji ini dengan bitbucket):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work
Kostano
sumber
4
Bagaimana bedanya dengan di atas, mengapa lebih baik?
Andy Hayden
1
Untuk beberapa alasan versi mkljun tidak mengurangi ruang git dalam kasus saya, saya sudah menghapus file dari indeks dengan menggunakan 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.
Kostanos
1
Ini benar-benar membantu tetapi saya perlu menggunakan -fopsi tidak hanya di -rfsini dan git rm --cached -rf --ignore-unmatch oops.isobukan git rm --cached -r --ignore-unmatch oops.isosebagai per @ lfender6445 di bawah ini
drstevok
10

Perhatikan 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.

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
mkljun
sumber
11
JANGAN menjalankan perintah ini kecuali jika Anda ingin membuat rasa sakit yang luar biasa untuk diri Anda sendiri. Itu menghapus banyak file kode sumber asli saya. Saya berasumsi itu akan membersihkan beberapa file besar dari histori komit saya di GIT (sesuai pertanyaan awal), namun, saya pikir perintah ini dirancang untuk secara permanen membersihkan file dari pohon kode sumber asli Anda (perbedaan besar!). Sistem saya: Windows, VS2012, Penyedia Kontrol Sumber Git.
Contango
2
Saya menggunakan perintah ini: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allalih-alih yang pertama dari kode Anda
Kostanos
8

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

Soheil
sumber
7

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-DIRECTORYdengan 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/

lfender6445
sumber
1
Tanggapan ini membantu saya, kecuali skrip dalam jawaban memiliki sedikit masalah dan tidak mencari di semua cabang dari saya. Tetapi perintah di tautan melakukannya dengan sempurna.
Ali B
5

Ini akan menghapusnya dari riwayat Anda

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
berkilau
sumber
Ini berhasil buat saya, terima kasih !!
Sonja Brits
Ini berfungsi dalam kasus saya. Saya menjalankan ini di cabang master Anda.
S. Domeng
4

Saya pada dasarnya melakukan apa yang ada di jawaban ini: https://stackoverflow.com/a/11032521/1286423

(untuk sejarah, saya akan salin-tempel di sini)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

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 treeobjek yang menunjuk ke file tersebut. Solusi utama saya untuk benar-benar membunuhnya adalah dengan:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

Repo saya .gitberubah dari 32MB menjadi 388KB, bahkan cabang-filter pun tidak bisa dibersihkan.

Dolanor
sumber
4

git filter-branchadalah 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.

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master
zhangyu12
sumber
2

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) .

Nir
sumber
Metode ini terlalu lambat untuk repositori besar. Butuh waktu lebih dari satu jam untuk mendaftar file-file besar. Kemudian ketika saya pergi untuk menghapus file, setelah satu jam hanya 1/3 dari jalan melalui pemrosesan file pertama yang ingin saya hapus.
kristianp
Ya, ini lambat, tetapi apakah berhasil ... Apakah Anda tahu sesuatu yang lebih cepat?
Nir
1
Belum pernah menggunakannya, tetapi BFG Repo-Cleaner, sesuai jawaban lain di halaman ini.
kristianp
2

Anda dapat melakukan ini menggunakan branch filterperintah:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD

John Foley
sumber
2

Ada jawaban yang sangat bagus di utas ini, tetapi sementara itu banyak dari mereka sudah usang. Penggunaan git-filter-branchtidak lagi direkomendasikan, karena sulit digunakan dan sangat lambat pada repositori besar.

git-filter-repo jauh lebih cepat dan mudah digunakan.

git-filter-repoadalah 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

git filter-repo --analyze

Ini membantu Anda menentukan apa yang harus dilakukan selanjutnya.

Anda dapat menghapus file DVD-rip Anda di mana saja:

 git filter-repo --invert-paths --path-match DVD-rip

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!

Donat
sumber
1

Ketika Anda mengalami masalah ini, git rmtidak 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 gcakan menyingkirkannya

Penggunaannya cukup sederhana git forget-blob file-to-forget. Anda bisa mendapatkan info lebih lanjut di sini

https://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!

nachoparker
sumber
Anda harus mendapatkan ini di homebrew
Cameron E
0

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-branchsambil memberikan fitur unik berikut:

  • Penulisan ulang cepat komit dan pohon (dengan urutan x10 hingga x100).
  • Dukungan bawaan untuk daftar putih dengan --keep (menyimpan file atau direktori) dan daftar hitam dengan opsi --hapus.
  • Penggunaan pola .gitignore like untuk pemfilteran pohon
  • Script C # yang cepat dan mudah untuk penyaringan komit dan penyaringan pohon
  • Dukungan untuk skrip dalam penyaringan pohon per pola file / direktori
  • Pangkas komit kosong / tidak berubah secara otomatis, termasuk gabungan komit
Philippe
sumber