git gc --aggressive vs git repack

91

Saya mencari cara untuk mengurangi ukuran gitrepositori. Pencarian membawa saya ke git gc --aggressivesebagian besar waktu. Saya juga membaca bahwa ini bukanlah pendekatan yang disukai.

Mengapa? apa yang harus saya waspadai jika saya sedang berlari gc --aggressive?

git repack -a -d --depth=250 --window=250direkomendasikan gc --aggressive. Mengapa? Bagaimana cara repackmengurangi ukuran repositori? Juga, saya tidak begitu jelas tentang bendera --depthdan --window.

Apa yang harus saya pilih antara gcdan repack? Kapan saya harus menggunakan gcdan repack?

Ajith R Nayak
sumber

Jawaban:

78

Saat ini tidak ada perbedaan: git gc --aggressiveberoperasi sesuai dengan saran yang dibuat Linus pada tahun 2007; Lihat di bawah. Pada versi 2.11 (Q4 2016), git defaultnya adalah kedalaman 50. Jendela dengan ukuran 250 bagus karena memindai bagian yang lebih besar dari setiap objek, tetapi kedalaman pada 250 buruk karena membuat setiap rantai merujuk ke sangat lama objek, yang memperlambat semua operasi git di masa mendatang untuk penggunaan disk yang sedikit lebih rendah.


Latar belakang sejarah

Linus menyarankan (lihat di bawah untuk posting milis lengkap) menggunakan git gc --aggressivehanya jika Anda memiliki, dalam kata-katanya, "paket yang sangat buruk" atau "delta yang sangat buruk," namun "hampir selalu, dalam kasus lain, itu sebenarnya sangat buruk sesuatu yang harus dikerjakan." Hasilnya bahkan dapat membuat repositori Anda dalam kondisi yang lebih buruk daripada saat Anda mulai!

Perintah yang dia sarankan untuk melakukan ini dengan benar setelah mengimpor "sejarah yang panjang dan terlibat" adalah

git repack -a -d -f --depth=250 --window=250

Tetapi ini mengasumsikan Anda telah menghapus gunk yang tidak diinginkan dari riwayat repositori Anda dan bahwa Anda telah mengikuti daftar periksa untuk menyusutkan repositori yang ditemukan dalam git filter-branchdokumentasi .

git-filter-branch dapat digunakan untuk membuang subset file, biasanya dengan beberapa kombinasi --index-filterdan --subdirectory-filter. Orang mengharapkan repositori yang dihasilkan lebih kecil dari aslinya, tetapi Anda memerlukan beberapa langkah lagi untuk membuatnya lebih kecil, karena Git berusaha keras untuk tidak kehilangan objek Anda sampai Anda memintanya. Pertama, pastikan bahwa:

  • Anda benar-benar menghapus semua varian nama file, jika blob dipindahkan selama masa pakainya. git log --name-only --follow --all -- filenamedapat membantu Anda menemukan penggantian nama.

  • Anda benar-benar memfilter semua ref: gunakan --tag-name-filter cat -- --allsaat menelepongit filter-branch .

Lalu ada dua cara untuk mendapatkan repositori yang lebih kecil. Cara yang lebih aman adalah dengan mengkloning, yang membuat dokumen asli Anda tetap utuh.

  • Kloning dengan git clone file:///path/to/repo. Klon tidak akan menghapus objek. Lihat git-clone. (Perhatikan bahwa kloning dengan jalur biasa hanya membuat semua tautan keras!)

Jika Anda benar-benar tidak ingin mengkloningnya, karena alasan apa pun, periksa poin-poin berikut (dalam urutan ini). Ini adalah pendekatan yang sangat merusak, jadi buatlah cadangan atau kembali untuk mengkloningnya. Anda telah diperingatkan.

  • Hapus referensi asli yang didukung oleh git-filter-branch: say

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • Kedaluwarsa semua reflog dengan git reflog expire --expire=now --all.

  • Sampah mengumpulkan semua objek yang tidak direferensikan dengan git gc --prune=now(atau jika Anda git gctidak cukup baru untuk mendukung argumen --prune, gunakan git repack -ad; git prunesaja).


Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST)
From: Linus Torvalds <torvalds at linux-foundation dot org>
To: Daniel Berlin <dberlin at dberlin dot org>
cc: David Miller <davem at davemloft dot net>,
    ismail at pardus dot org dot tr,
    gcc at gcc dot gnu dot org,
    git at vger dot kernel dot org
Subject: Re: Git and GCC
In-Reply-To: <[email protected]>
Message-ID: <[email protected]>
References: <[email protected]>
            <[email protected]>
            <[email protected]>
            <[email protected]>
            <[email protected]>

Pada Kamis, 6 Des 2007, Daniel Berlin menulis:

Sebenarnya, ternyata git-gc --aggressivemelakukan hal bodoh ini untuk mengemas file terkadang terlepas dari apakah Anda mengonversi dari repo SVN atau tidak.

Benar. git --aggressivekebanyakan bodoh. Ini benar-benar hanya berguna untuk kasus “Saya tahu saya punya benar - benar buruk, dan saya ingin membuang semua keputusan pengepakan yang buruk yang telah saya lakukan."

Untuk menjelaskan ini, ada baiknya menjelaskan (Anda mungkin menyadarinya, tapi biarkan saya membahas dasar-dasarnya) bagaimana rantai-delta git bekerja, dan bagaimana mereka sangat berbeda dari kebanyakan sistem lain.

Di SCM lain, rantai-delta umumnya diperbaiki. Ini mungkin "maju" atau "mundur," dan mungkin berkembang sedikit saat Anda bekerja dengan repositori, tetapi umumnya itu adalah rantai perubahan ke satu file yang direpresentasikan sebagai semacam entitas SCM tunggal. Di CVS, itu jelas*,v file, dan banyak sistem lain melakukan hal yang agak mirip.

Git juga melakukan rantai-delta, tetapi melakukannya dengan lebih “longgar”. Tidak ada entitas tetap. Delta dibuat berdasarkan versi acak lainnya yang dianggap git sebagai kandidat delta yang baik (dengan berbagai heuristik yang cukup berhasil), dan sama sekali tidak ada aturan pengelompokan yang ketat.

Ini umumnya merupakan hal yang sangat bagus. Ini bagus untuk berbagai alasan konseptual ( yaitu , git secara internal bahkan tidak pernah benar-benar perlu peduli dengan keseluruhan rantai revisi - tidak benar-benar memikirkan delta sama sekali), tetapi juga bagus karena menyingkirkan aturan delta yang tidak fleksibel berarti bahwa git tidak memiliki masalah sama sekali dengan menggabungkan dua file menjadi satu, misalnya - tidak ada *,v“file revisi” yang memiliki arti tersembunyi.

Ini juga berarti bahwa pilihan delta adalah pertanyaan yang jauh lebih terbuka. Jika Anda membatasi rantai delta menjadi hanya satu file, Anda benar-benar tidak memiliki banyak pilihan tentang apa yang harus dilakukan tentang delta, tetapi di git, ini bisa menjadi masalah yang sama sekali berbeda.

Dan di sinilah nama yang sangat buruk --aggressive . Meskipun git biasanya mencoba menggunakan kembali informasi delta (karena itu ide yang bagus, dan tidak membuang waktu CPU untuk menemukan kembali semua delta bagus yang kami temukan sebelumnya), terkadang Anda ingin mengatakan “mari kita mulai dari awal, dengan slate kosong, dan abaikan semua informasi delta sebelumnya, dan coba buat sekumpulan delta baru”.

Jadi --aggressivebukan tentang menjadi agresif, tetapi tentang membuang-buang waktu CPU untuk melakukan kembali keputusan yang telah kita lakukan sebelumnya!

Terkadang itu adalah hal yang baik. Beberapa alat impor khususnya dapat menghasilkan delta yang sangat buruk. Apa pun yang menggunakan git fast-import, misalnya, kemungkinan besar tidak memiliki tata letak delta yang bagus, jadi sebaiknya Anda mengatakan "Saya ingin memulai dari yang bersih".

Tapi hampir selalu, dalam kasus lain, itu sebenarnya hal yang sangat buruk untuk dilakukan. Ini akan membuang-buang waktu CPU, dan terutama jika Anda benar-benar telah melakukan pekerjaan delta dengan baik sebelumnya, hasil akhirnya tidak akan menggunakan kembali semua delta bagus yang sudah Anda temukan, jadi Anda akan mendapatkan banyak hasil akhir yang lebih buruk juga!

Saya akan mengirim tambalan ke Junio ​​untuk menghapus git gc --aggressive dokumentasinya. Ini bisa berguna, tetapi umumnya berguna hanya ketika Anda benar-benar memahami pada tingkat yang sangat dalam apa yang dilakukannya, dan dokumentasi itu tidak membantu Anda melakukannya.

Secara umum, melakukan incremental git gcadalah pendekatan yang tepat, dan lebih baik daripada melakukan git gc --aggressive. Ini akan menggunakan kembali delta lama, dan ketika delta lama itu tidak dapat ditemukan (alasan untuk melakukan GC inkremental di tempat pertama!) Itu akan membuat yang baru.

Di sisi lain, memang benar bahwa "impor awal dari sejarah yang panjang dan terlibat" adalah titik di mana menghabiskan banyak waktu untuk menemukan delta yang benar - benar bagus akan bermanfaat . Kemudian, setiap pengguna setelahnya (selama mereka tidak menggunakannya git gc --aggressiveuntuk mengurungkannya!) Akan mendapatkan keuntungan dari peristiwa satu kali tersebut. Jadi, terutama untuk proyek besar dengan sejarah panjang, mungkin ada baiknya melakukan beberapa pekerjaan ekstra, memberi tahu kode pencarian delta untuk menjadi liar.

Jadi yang setara dengan git gc --aggressive- tetapi dilakukan dengan benar - adalah melakukan (dalam semalam) sesuatu seperti

git repack -a -d --depth=250 --window=250

di mana kedalaman itu hanya tentang seberapa dalam rantai delta bisa (membuatnya lebih panjang untuk sejarah lama - itu sepadan dengan overhead ruang), dan masalah jendela adalah tentang seberapa besar jendela objek yang kita inginkan untuk dipindai setiap kandidat delta.

Dan di sini, Anda mungkin ingin menambahkan -fflag (yang merupakan "hapus semua delta lama", karena Anda sekarang sebenarnya mencoba memastikan bahwa yang ini benar-benar menemukan kandidat yang baik.

Dan kemudian itu akan memakan waktu selamanya dan satu hari ( yaitu , hal "lakukan dalam semalam"). Tetapi hasil akhirnya adalah setiap orang yang berada di hilir dari repositori itu akan mendapatkan paket yang jauh lebih baik, tanpa harus mengeluarkan tenaga untuk itu sendiri.

          Linus
Greg Bacon
sumber
2
Komentar Anda tentang kedalaman agak membingungkan. Pada awalnya saya akan mengeluh bahwa Anda salah besar, bahwa agresif dapat sangat mempercepat repositori git. Setelah melakukan pengumpulan sampah yang agresif, repo BESAR yang membutuhkan waktu lima menit untuk melakukan status git dikurangi menjadi detik. Tapi kemudian saya menyadari Anda tidak bermaksud gc agresif memperlambat repo, tetapi hanya ukuran kedalaman yang sangat besar.
pengguna6856
58

Kapan saya harus menggunakan gc & repack?

Seperti yang saya sebutkan dalam " Pengumpulan Sampah Git tampaknya tidak sepenuhnya berfungsi ", a git gc --aggressivetidak cukup atau bahkan tidak cukup.
Dan, seperti yang saya jelaskan di bawah , seringkali tidak diperlukan.

Kombinasi paling efektif adalah dengan menambahkan git repack, tetapi juga git prune:

git gc
git repack -Ad      # kills in-pack garbage
git prune           # kills loose garbage

Catatan: Git 2.11 (Q4 2016) akan mengatur gc aggressivekedalaman default menjadi 50

Lihat commit 07e7dbf (11 Agustus 2016) oleh Jeff King ( peff) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit 0952ca8 , 21 Sep 2016)

gc: kedalaman agresif default menjadi 50

" git gc --aggressive" digunakan untuk membatasi panjang rantai-delta hingga 250, yang terlalu dalam untuk mendapatkan penghematan ruang tambahan dan merugikan kinerja runtime.
Batasnya telah dikurangi menjadi 50.

Ringkasannya adalah: default saat ini sebesar 250 tidak menghemat banyak ruang, dan membutuhkan biaya CPU. Ini bukan pengorbanan yang baik.

--aggressiveBendera " " untuk git-gcmelakukan tiga hal:

  1. gunakan " -f" untuk membuang delta yang ada dan menghitung ulang dari awal
  2. gunakan "--window = 250" untuk mencari delta dengan lebih cermat
  3. gunakan "--depth = 250" untuk membuat rantai delta yang lebih panjang

Item (1) dan (2) cocok untuk repack "agresif".
Mereka meminta repack untuk melakukan lebih banyak pekerjaan komputasi dengan harapan mendapatkan paket yang lebih baik. Anda membayar biaya selama mengemas kembali, dan operasi lain hanya melihat manfaatnya.

Butir (3) tidak begitu jelas.
Mengizinkan rantai yang lebih panjang berarti lebih sedikit pembatasan pada delta, yang berarti berpotensi menemukan yang lebih baik dan menghemat ruang.
Tetapi itu juga berarti bahwa operasi yang mengakses delta harus mengikuti rantai yang lebih panjang, yang memengaruhi kinerjanya.
Jadi ini tradeoff, dan tidak jelas apakah trade-off itu bagus.

(Lihat berkomitmen untuk belajar )

Anda dapat melihat bahwa penghematan CPU untuk operasi reguler meningkat seiring dengan penurunan kedalaman.
Tetapi kita juga dapat melihat bahwa penghematan ruang tidak terlalu bagus karena kedalamannya semakin tinggi. Menghemat 5-10% antara 10 dan 50 mungkin sepadan dengan pengorbanan CPU. Menyimpan 1% untuk beralih dari 50 ke 100, atau 0,5% lagi untuk beralih dari 100 ke 250 mungkin tidak.


Berbicara tentang penyimpanan CPU, " git repack" belajar untuk menerima --threads=<n>opsi dan meneruskannya ke objek paket.

Lihat commit 40bcf31 (26 Apr 2017) oleh Junio ​​C Hamano ( gitster) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit 31fb6f4 , 29 Mei 2017)

repack: terima --threads=<n>dan berikan kepack-objects

Kami sudah melakukannya untuk --window=<n>dan --depth=<n>; ini akan membantu ketika pengguna ingin memaksakan --threads=1pengujian yang dapat direproduksi tanpa terpengaruh oleh perlombaan beberapa utas.

VonC
sumber
3
Saya menyebutkan utas Linus di tautan "Pengumpulan Sampah Git tampaknya tidak berfungsi sepenuhnya"
VonC
1
Terima kasih atas pembaruan modern ini! Setiap jawaban lain di sini sudah tua. Sekarang kita dapat melihat bahwa git gc --aggressivetelah diperbaiki dua kali: Pertama, untuk melakukan apa yang disarankan Linus pada tahun 2007 sebagai "metode pengemasan yang lebih baik". Dan kemudian di Git 2.11 untuk menghindari kedalaman objek yang berlebihan seperti yang disarankan Linus, tetapi ternyata berbahaya (memperlambat semua operasi Git di masa mendatang dan tidak menghemat ruang yang layak untuk dibicarakan).
gw0
git gc, diikuti oleh git repack -Ad dan git prune meningkatkan ukuran repositori saya ... mengapa?
Devops
@devops Tidak yakin: versi Git apa yang Anda gunakan? Anda dapat mengajukan pertanyaan baru untuk itu (dengan detail lebih lanjut seperti OS, ukuran umum repo Anda, ...)
VonC
man git-repackmengatakan untuk -d: `Juga jalankan git prune-pack untuk menghapus file objek longgar yang berlebihan.` Atau apakah git prunejuga melakukan itu? man git-pruneberkata In most cases, users should run git gc, which calls git prune., jadi apa gunanya setelah itu git gc? Bukankah lebih baik atau cukup hanya digunakan git repack -Ad && git gc?
Jakob
14

Masalahnya git gc --aggressiveadalah bahwa nama opsi dan dokumentasinya menyesatkan.

Seperti yang dijelaskan Linus sendiri dalam surat ini , yang git gc --aggressivepada dasarnya dilakukannya adalah:

Meskipun git biasanya mencoba menggunakan kembali informasi delta (karena itu ide yang bagus, dan tidak membuang waktu CPU untuk menemukan kembali semua delta bagus yang kami temukan sebelumnya), terkadang Anda ingin mengatakan "mari kita mulai dari awal, dengan slate kosong, dan abaikan semua informasi delta sebelumnya, dan coba buat sekumpulan delta baru ".

Biasanya tidak perlu menghitung ulang delta di git, karena git menentukan delta ini sangat fleksibel. Ini hanya masuk akal jika Anda tahu bahwa Anda memiliki delta yang sangat, sangat buruk. Seperti yang dijelaskan Linus, sebagian besar alat yang digunakan git fast-importtermasuk dalam kategori ini.

Sebagian besar waktu git melakukan pekerjaan yang cukup baik dalam menentukan delta yang berguna dan penggunaan git gc --aggressiveakan meninggalkan Anda dengan delta yang berpotensi lebih buruk lagi sambil membuang banyak waktu CPU.


Linus mengakhiri emailnya dengan kesimpulan bahwa git repackdengan pilihan besar --depthdan --windowlebih baik di sebagian besar waktu; terutama setelah Anda mengimpor proyek besar dan ingin memastikan bahwa git menemukan delta yang bagus.

Jadi yang setara dengan git gc --aggressive- tetapi dilakukan dengan benar - adalah melakukan (dalam semalam) sesuatu seperti

git repack -a -d --depth=250 --window=250

di mana kedalaman itu hanya tentang seberapa dalam rantai delta bisa (membuatnya lebih panjang untuk sejarah lama - itu sepadan dengan overhead ruang), dan masalah jendela adalah tentang seberapa besar jendela objek yang kita inginkan untuk dipindai setiap kandidat delta.

Dan di sini, Anda mungkin ingin menambahkan -fbendera (yang merupakan "hapus semua delta lama", karena Anda sekarang sebenarnya mencoba memastikan bahwa yang ini benar-benar menemukan kandidat yang baik.

Sascha Wolf
sumber
9

Peringatan. Jangan dijalankan git gc --agressivedengan repositori yang tidak disinkronkan dengan remote jika Anda tidak memiliki cadangan.

Operasi ini membuat ulang delta dari awal dan dapat menyebabkan hilangnya data jika terganggu dengan baik.

Untuk komputer 8GB saya, gc agresif kehabisan memori pada repositori 1Gb dengan 10k komit kecil. Ketika OOM killer menghentikan proses git - itu meninggalkan saya dengan repositori yang hampir kosong, hanya pohon yang berfungsi dan beberapa delta yang bertahan.

Tentu saja, itu bukan satu-satunya salinan repositori jadi saya hanya membuatnya ulang dan menarik dari jarak jauh (pengambilan tidak berfungsi pada repo yang rusak dan menemui jalan buntu pada langkah 'menyelesaikan delta' beberapa kali saya mencoba melakukannya), tetapi jika repo Anda adalah repo lokal pengembang tunggal tanpa remote sama sekali - buat cadangannya terlebih dahulu.

Penunjuk Sage
sumber
5

Catatan: berhati-hatilah dalam menggunakan git gc --aggressive, seperti yang dijelaskan oleh Git 2.22 (Q2 2019).

Lihat komit 0044f77 , komit daecbf2 , komit 7.384.504 , komit 22d4e3b , komit 080a448 , komit 54d56f5 , komit d257e0f , komit b6a8d09 (7 April 2019), dan komit fc559fb , komit cf9cd77 , komit b11e856 (22 Mar 2019) oleh Ævar Arnfjord Bjarmason ( avar) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit ac70c53 , 25 Apr 2019)

gc docs: meremehkan kegunaan --aggressive

Dokumen " gc --aggressive" yang ada hanya sedikit menyarankan kepada pengguna bahwa mereka menjalankannya secara teratur.
Saya secara pribadi telah berbicara dengan banyak pengguna yang telah menggunakan dokumen ini sebagai saran untuk menggunakan opsi ini, dan biasanya (kebanyakan) membuang-buang waktu .

Jadi mari kita perjelas apa yang sebenarnya dilakukannya, dan biarkan pengguna menarik kesimpulan mereka sendiri.

Mari kita juga memperjelas "Efek [...] tetap ada" untuk memparafrasekan versi singkat penjelasan Jeff King .

Artinya, dokumentasi git-gc sekarang menyertakan :

AGRESIF

Ketika --aggressiveopsi diberikan, git-repackakan dipanggil dengan -fflag, yang pada gilirannya akan diteruskan --no-reuse-deltake git-pack-objects .
Ini akan membuang semua delta yang ada dan menghitungnya kembali, dengan mengorbankan lebih banyak waktu untuk mengemas ulang.

Efek dari hal ini sebagian besar tetap ada, misalnya ketika paket dan objek lepas digabungkan menjadi satu paket lainnya, delta yang ada dalam paket tersebut mungkin dapat digunakan kembali, tetapi ada juga berbagai kasus di mana kita mungkin memilih delta sub-optimal dari yang lebih baru pak sebagai gantinya.

Selanjutnya, memasok --aggressiveakan mengubah opsi --depthdan yang --windowditeruskan ke git-repack.
Lihat gc.aggressiveDepthdan gc.aggressiveWindowpengaturan di bawah ini.
Dengan menggunakan ukuran jendela yang lebih besar, kita cenderung menemukan delta yang lebih optimal.

Mungkin tidak ada gunanya menggunakan opsi ini pada repositori tertentu tanpa menjalankan tolok ukur kinerja yang disesuaikan di atasnya .
Dibutuhkan lebih banyak waktu, dan pengoptimalan ruang / delta yang dihasilkan mungkin sepadan atau mungkin tidak sepadan. Tidak menggunakan ini sama sekali adalah trade-off yang tepat untuk sebagian besar pengguna dan repositori mereka.

Dan ( lakukan 080a448 ):

gcdokumen: perhatikan bagaimana --aggressivedampak --window&--depth

Sejak 07e7dbf ( gc: kedalaman agresif default ke 50, 2016-08-11, Git v2.10.1) kami agak membingungkan menggunakan kedalaman yang sama di bawah --aggressiveseperti yang kami lakukan secara default.

Seperti disebutkan dalam commit yang masuk akal, adalah salah untuk membuat lebih mendalam default untuk "agresif", dan dengan demikian menghemat ruang disk dengan mengorbankan kinerja runtime, yang biasanya kebalikan dari seseorang yang menginginkan "gc agresif" keinginan.

VonC
sumber