Başvurulmayan blobları git depomdan nasıl kaldırırım


124

Ana ve yayın - iki dalı olan bir GitHub depom var.

Yayın dalı, çok büyük bir depo boyutuna (> 250MB) katkıda bulunan ikili dağıtım dosyalarını içeriyordu, bu yüzden işleri temizlemeye karar verdim.

Önce uzaktan yayın şubesini sildim. git push origin :release

Sonra yerel yayın şubesini sildim. İlk önce denedim git branch -d release, ama git dedi "hata: Dal 'yayın' şu anki HEAD'inizin atası değil." bu doğru, bu yüzden git branch -D releaseonu silinmeye zorladım.

Ancak hem yerel hem de GitHub'daki depo boyutum hala çok büyüktü. Sonra da git komutlarının olağan listesinden git gc --prune=today --aggressiveşanssız bir şekilde geçtim .

Charles Bailey'nin SO 1029969'daki talimatlarını takip ederek en büyük bloblar için SHA1'lerin bir listesini elde edebildim. Daha sonra blobları bulmak için SO 460331'den gelen komut dosyasını kullandım ... ve en büyük beşi mevcut değil, daha küçük bloblar bulunsa da, betiğin çalıştığını biliyorum.

Bence bu bloglar sürüm şubesinden gelen ikili dosyalar ve bu şubenin silinmesinden sonra bir şekilde etrafta kaldılar. Onlardan kurtulmanın doğru yolu nedir?


Git'in hangi sürümünü kullanıyorsunuz? Ve stackoverflow.com/questions/1106529/… 'yi denediniz mi?
VonC

git sürüm 1.6.2.3 gc'yi ve çeşitli argümanlarla budamayı denedim. Yeniden paketlemeyi denemedim -a -d -l, sadece çalıştırdım, değişiklik yok.
kkrugler

2
Yeni bilgi - GitHub'dan yeni bir klon artık referans verilmeyen bloblara sahip değil ve 250MB'den "sadece" 84MB'ye düştü.
kkrugler

Yanıtlar:


219

... ve daha fazla uzatmadan, size "git-gc-all" adlı bu yararlı komutu sunabilir miyim, ekstra yapılandırma değişkenleri gelene kadar tüm git çöplerinizi kaldırmayı garanti eder :

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

Ayrıca önce bunlara benzer bir şey çalıştırmanız gerekebilir, ah canım, git karmaşıktır !!

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ | xargs -n1 --no-run-if-empty git update-ref -d

Ayrıca bazı etiketleri kaldırmanız gerekebilir, teşekkürler Zitrax:

git tag | xargs git tag -d

Bütün bunları bir senaryoya koydum: git-gc-all-vahşi .


1
İlginç. Daha genel cevabıma iyi bir alternatif. +1
VonC

10
Bu daha fazla oy hak ediyor. Sonunda diğer yöntemlerin saklayacağı birçok git nesnesinden kurtuldu. Teşekkürler!
Jean-Philippe Pellet

1
Upvoted. Vay canına, az önce ne yaptığımı bilmiyorum ama çok temiz görünüyor. Ne yaptığını açıklar mısınız? İçimde her şeyi temizlediğini hissediyorum objects. Bunlar nelerdir ve neden (görünüşte) alakasızlar?
Redsandro

2
@Redsandro, anladığım kadarıyla, bu "git rm origin", "rm" ve "git update-ref -d" komutları, uzaktan kumandalar için eski işlemlere olan referansları kaldırıyor, bu da çöp toplamayı engelliyor olabilir. "Git gc" seçenekleri, çeşitli eski işlemlere bağlı kalmamasını söyler, aksi takdirde bir süre onları tutacaktır. Örneğin gc.rerereresolved, varsayılan olarak 60 gün süreyle saklanan "daha önce çözdüğünüz çakışan birleştirme kayıtları" içindir. Bu seçenekler git-gc kılavuz sayfasındadır. Git konusunda uzman değilim ve tüm bunların ne işe yaradığını tam olarak bilmiyorum. Onları yönetim sayfalarından buldum ve commit refs için .git'i buldum.
Sam Watkins

1
Git nesnesi, geçmişten eski şeyler de dahil olmak üzere git deponuzdaki sıkıştırılmış bir dosya veya ağaç veya işlemdir. git gc gereksiz nesneleri temizler. Mevcut deponuz ve geçmişiniz için hala gerekli olan nesneleri tutar.
Sam Watkins

81

Gibi tarif burada , kalıcı sadece reflog yoluyla başvurulan her şeyi kaldırmak istiyorsanız , basitçe kullanımı

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

git reflog expire --expire-unreachable=now --allulaşılamayan kayıtların tüm referanslarını kaldırır reflog.

git gc --prune=now taahhütleri kendileri kaldırır.

Dikkat : Yalnızca kullanmak git gc --prune=nowişe yaramayacaktır çünkü bu işlemlere reflog'da hala başvurulmaktadır. Bu nedenle, reflogun temizlenmesi zorunludur. Ayrıca, kullanırsanız rerere, bu komutlarla temizlenmeyen ek referanslara sahip olduğunu unutmayın . Daha git help rererefazla ayrıntı için bakın. Buna ek olarak, yerel veya uzak dallar veya etiketler tarafından referans verilen hiçbir işlem, git tarafından değerli veriler olarak kabul edildiğinden kaldırılmayacaktır.


14
İşe yaradı ama bir türlü (benim durumumda diğerleri için, sadece dikkatli önemli bir şey) sürecinde kaydettiğim stashes kaybetti
Amro

1
neden agresif değil?
JoelFan

3
Bence bu cevabın net bir uyarıya ihtiyacı var, tercihen en üstte. Düzenleme önerim reddedildi, çünkü sanırım bunu yazara bir yorumda önermeliyim? Lütfen stackoverflow.com/review/suggested-edits/26023988 bu düzenlemeyi kabul edin veya kendi yönteminize göre bir uyarı ekleyin. Ayrıca, bu tüm zulalarınızı düşürür . Bu da uyarıda unutulmamalıdır!
Inigo

Git sürüm 2.17 ile test ettim ve saklanan kayıtlar yukarıdaki komutlar tarafından kaldırılmayacak. Herhangi bir ek komut çalıştırmadığınızdan emin misiniz?
Mikko Rantalainen

1
git fetch --pruneyerel bloblar silinerek boyutu daha da küçültün.
hectorpal

33

Bahsedildiği gibi bu SO cevap , git gcaslında repo boyutunu artırabilirsiniz!

Ayrıca bu konuya bakın

Şimdi git bir güvenlik mekanizması vardır değil 'çalıştırırken hemen silme başvurulmayan nesneleri git gc'.
Varsayılan olarak, başvurulmayan nesneler 2 hafta boyunca tutulur. Bu, yanlışlıkla silinen dalları veya taahhütleri kurtarmanızı kolaylaştırmak veya süreçte yeni oluşturulmuş ancak henüz referans verilmemiş bir nesnenin git gcparalel olarak çalışan bir ' ' işlemle silinebileceği bir yarıştan kaçınmak içindir .

Dolayısıyla, paketlenmiş ancak referansta bulunulmayan nesnelere bu yetkisiz dönemi vermek için, yeniden paketleme işlemi bu referansta bulunulmayan nesneleri paketin dışına, yaşlanabilmeleri ve sonunda budamaları için gevşek biçimlerine iter.
Referans alınmayan nesneler genellikle çok fazla değildir. 404855 referans alınmayan nesnelere sahip olmak oldukça fazla ve bu nesneleri ilk etapta bir klon aracılığıyla göndermek aptalca ve ağ bant genişliğinin tamamen boşa harcanması.

Her neyse ... Sorununuzu çözmek için, bu yetkisiz kullanım süresini devre dışı bırakmak ve bu başvurulmayan nesnelerden hemen kurtulmak git gciçin --prune=nowargüman ile koşmanız yeterlidir (yalnızca aynı anda başka git etkinlikleri gerçekleşmiyorsa güvenli olmalıdır) bir iş istasyonunda sağlanması kolay olmalıdır).

Ve BTW, git gc --aggressivedaha sonraki bir git sürümüyle (veya ' git repack -a -f -d --window=250 --depth=250') ' ' kullanarak

Aynı diş söz :

 git config pack.deltaCacheSize 1

Bu, delta önbellek boyutunu varsayılan 0 yerine bir bayta (etkin bir şekilde devre dışı bırakarak) sınırlar, bu da sınırsız anlamına gelir. Bununla git repackbirlikte, 4GB RAM'li bir x86-64 sisteminde yukarıdaki komutu kullanarak ve 4 iş parçacığı kullanarak (bu bir dört çekirdekli) bu depoyu yeniden paketleyebiliyorum . Yerleşik bellek kullanımı yine de yaklaşık 3,3 GB'a çıkar.

Makineniz SMP ise ve yeterli RAM'e sahip değilseniz, iş parçacığı sayısını yalnızca bire düşürebilirsiniz:

git config pack.threads 1

Ek olarak, bellek kullanımını --window-memory argument' git repack' ile daha da sınırlayabilirsiniz .
Örneğin --window-memory=128M, depo çok sayıda büyük dosya içeriyorsa , kullanmak delta arama belleği kullanımında makul bir üst sınır tutmalıdır, ancak bu, depo çok sayıda büyük dosya içeriyorsa daha az optimum delta eşleşmesine neden olabilir.


Filtre dalı cephesinde, bu komut dosyasını (dikkatli bir şekilde) düşünebilirsiniz.

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

stackoverflow.com/questions/359424/… ayrıca filter-branchkomut kullanımı için iyi bir başlangıçtır .
VonC

Merhaba VonC - Git gc prune = şimdi şanssız bir şekilde denedim. Bu gerçekten bir git hatasına benziyor, çünkü bir şube silme işleminin ardından yerel olarak başvurulmamış blob'larla karşılaştım, ancak bunlar GitHub deposunun yeni bir klonuyla orada değiller ... yani bu sadece yerel bir depo sorunu. Ama temizlemek istediğim ek dosyalarım var, bu yüzden yukarıda bahsettiğiniz komut dosyası harika - teşekkürler!
kkrugler


12

HEAD'iniz her hareket ettiğinde, git bunu reflog. Kaydetmeleri kaldırdıysanız, hala reflog~ 30 gün boyunca tarafından referans alındığından "sarkan kaydetmeler "iniz vardır . Bu, taahhütleri kazara sildiğinizde güvenlik ağıdır.

Sen kullanabilirsiniz git reflogkomut kaldır özgü kaydedilmesini, repack, vb .., ya da sadece üst düzey komutunu:

git gc --prune=now

5

Kullanabilirsiniz git forget-blob.

Kullanımı oldukça basit git forget-blob file-to-forget. Buradan daha fazla bilgi edinebilirsiniz

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

Geçmişinizdeki, yeniden günlüğünüzdeki, etiketlerinizdeki tüm işlemlerden kaybolacaktır.

Arada sırada aynı problemle karşılaşıyorum ve her seferinde bu yazıya ve diğerlerine geri dönmem gerekiyor, bu yüzden süreci otomatikleştirdim.

Sam Watkins gibi katkıda bulunanlara verilen krediler


2

Git-filter-branch kullanmayı deneyin - büyük blobları kaldırmaz, ancak belirttiğiniz büyük dosyaları depodan kaldırabilir. Benim için repo boyutunu yüzlerce MB'den 12 MB'a düşürüyor.


6
Şimdi bu korkutucu bir komut :) Git-fu'm daha güçlü hissettiğinde bunu denemem gerekecek.
kkrugler

onu tekrar söyleyebilirsin. Bir arşivin geçmişini değiştiren komutlara karşı her zaman temkinli davranırım. Birden çok kişi bu depodan itip çekerken ve birden bir sürü nesnenin beklediği bir sürü nesne orada olmadığında işler çok ters gitme eğilimindedir.
Jonathan Dumanie'nin

1

Bazen, "gc" nin pek işe yaramamasının nedeni, eski bir işleme dayalı tamamlanmamış bir yeniden ödeme veya zula olmasıdır.


Ya da eski kayıt HEAD, ORIG_HEAD, FETCH_HEAD, reflog veya git'in otomatik olarak değerli hiçbir şeyi kaybetmemesini sağlamaya çalıştığı başka bir şey tarafından referans alınır. Bunların hepsini gerçekten kaybetmek istiyorsanız, bunu yapmak için fazladan yol kat etmeniz gerekir.
Mikko Rantalainen

1

Başka bir ipucu eklemek için git gc'yi kullanmadan önce uzaktan kumandalarınızın eski dallarını silmek için git remote prune kullanmayı unutmayın.

onları git şubesi ile görebilirsiniz -a

Genellikle github ve çatallı depolardan aldığınızda kullanışlıdır ...


1

Yapmadan önce git filter-branchve git gcdeponuzda bulunan etiketleri incelemelisiniz. Sürekli entegrasyon ve dağıtım gibi şeyler için otomatik etiketleme özelliğine sahip herhangi bir gerçek sistem, istenmeyen nesneleri bu etiketlerle referans olarak göstermeye devam eder, bu nedenle gconları kaldıramaz ve yine de deponun boyutunun neden bu kadar büyük olduğunu merak etmeye devam edersiniz.

En iyi yolu tüm un-istedim şeyler kurtulmak için çalıştırmaktır git-filter& git gcve sonra yeni bir çıplak repo usta itin. Yeni çıplak depoda temizlenmiş ağaç olacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.