Klasörü ve içeriğini git / GitHub'ın geçmişinden kaldırma


318

GitHub hesabımdaki bir havuz üzerinde çalışıyordum ve bu durum tökezlediğim bir sorun.

  • Birkaç npm paketi kurulu bir klasör içeren Node.js projesi
  • Paketler node_modulesklasördeydi
  • Git deposuna bu klasörü ekledi ve kodu github'a itti (o sırada npm kısmı hakkında düşünmüyordu)
  • Kodun bir parçası olmak için bu klasöre gerçekten ihtiyaç duymadığınızı fark ettiniz
  • O klasörü sildi, itti

Bu durumda, toplam git repo'sunun boyutu 6MB civarındaydı ve burada gerçek kod (bu klasör hariç tümü) sadece 300 KB civarındaydı .

Şimdi sonunda aradığım şey, git paketinin geçmişinden bu paket klasörün ayrıntılarından kurtulmanın bir yoludur, bu yüzden birisi onu klonlarsa, alacakları gerçek dosyaların nereden alınacağını 6mb değerinde tarih indirmek zorunda kalmazlar. son taahhüt itibariyle 300KB olacaktır.

Bunun için olası çözümlere baktım ve bu 2 yöntemi denedim

Gist senaryoyu çalıştırdıktan sonra o klasörden kurtulduğunu ve bundan sonra 50 farklı taahhüdün değiştirildiğini gösterdi. Ama bu kodu itmeme izin vermedi. Ben itmeye çalıştığımda, dedi Branch up to dateama 50 taahhütleri a git status. Diğer 2 yöntem de yardımcı olmadı.

Şimdi o klasörün geçmişinden kurtulduğunu göstermesine rağmen, localhost'umdaki bu repo büyüklüğünü kontrol ettiğimde, hala 6MB civarındaydı. (Ayrıca refs/originalklasörü sildim ama repo boyutundaki değişikliği görmedim).

Açıklığa kavuşturmaya çalıştığım şey, yalnızca taahhüt geçmişinden kurtulmanın bir yolu varsa (ki bu olduğunu düşündüğüm tek şey) değil, aynı zamanda bu dosyaların git bir geri almak istediğini varsayarak tutuyor.

Bunun için bir çözüm sunulduğunu ve localhost'umda uygulandığını ancak GitHub repo'suna çoğaltılamayacağını söyleyelim, bu repoyu klonlamak, ilk işleme geri dönme hile yapmak ve itmek (veya git anlamına gelir mi) hala tüm bu taahhütlerin bir geçmişi var mı? - aka. 6MB).

Buradaki son hedefim, temelde git'ten klasör içeriğinden kurtulmanın en iyi yolunu bulmak, böylece bir kullanıcının 6MB değerinde bir şey indirmek zorunda kalmaması ve yine de modüller klasörüne asla dokunmayan diğer taahhütlere sahip olması (bu güzel bunların çoğu) git'in tarihinde.

Bunu nasıl yapabilirim?


3
Aşağıdaki cevaplardan herhangi biri sorununuzu çözdüyse, belki de sorunuzu cevap olarak kabul etmeyi düşünmelisiniz. meta.stackexchange.com/questions/5234/…
starbeamrainbowlabs 22:17

Yanıtlar:


556

Kodu kopyalayıp yapıştırmak için buradaysanız:

Bu, node_modulestarihe karışan bir örnektir

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

Git aslında ne yapar:

İlk satır --tree-filter, komutu çalıştırarak HEAD (geçerli dalınız) ile aynı ağaçtaki ( ) tüm referansları yineler rm -rf node_modules. Bu komut, node_modules klasörünü siler (klasör -rolmadan -r, rmklasörleri silmez), kullanıcıya herhangi bir komut istemi ( -f) vermez . Eklenen --prune-emptyişe yaramaz silmeler (hiçbir şeyi değiştirmez) özyineli olarak taahhüt eder.

İkinci satır, o eski dal için referansı siler.

Komutların geri kalanı nispeten basittir.


3
Sadece bir yan not: git count-objects -vDosyaların gerçekten kaldırılıp kaldırılmadığını kontrol ederdim , ancak havuzu tekrar klonlayana kadar deponun boyutu aynı kalır. Git bence tüm orijinal dosyaların bir kopyasını tutar.
Davide Icardi

4
Antik olmayan bir git ile, bu muhtemelen okumalı --force-with-lease, değil --force.
Griwes

4
Bu komutların hiçbiri pencerelerde çalışmaz. Ya da en azından Windows 10, "kes ve yapıştır" ın çalıştığı işletim sistemini
David

3
Windows 10 kullanıcıları için, bu Windows için Bash altında güzel çalışır (Ubuntu kullandım)
Andrej Kyselica

3
Windows kabuğu ve git bash ile denedim ve işe yaramadı. İlk komut geçişi, ikinci komut başarısız!
Mohy Eldeen

240

Bunu bulmak --tree-filterdiğer cevaplar kullanılan seçenek özellikle kaydedilmesini ve birçok büyük kaynaklara üzerinde, çok yavaş olabilir.

İşte --index-filterçok daha hızlı çalışan seçeneği kullanarak git geçmişinden bir dizini tamamen kaldırmak için kullandığım yöntem :

# 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

Deponun boyutunu önce ve sonra gcile kontrol edebilirsiniz:

git count-objects -vH

3
bunun neden daha hızlı olduğunu açıklayabilir misiniz?
knocte

7
@knocte: belgelerden ( git-scm.com/docs/git-filter-branch ). "--index-filter: ... ağaç filtresine benzer, ancak ağacı kontrol etmez, bu da çok daha hızlı yapar"
Lee Netherton

23
Bu neden kabul edilen cevap değil? Çok kapsamlı.
Mad Physicist

2
Bunu Windows'ta yapıyorsanız, tek tırnak yerine çift tırnak gerekir.
Kris Morness

12
Geçme --quietiçin git rmfaktör 4. en azından yukarıda değinilen hızlandırdı benim yeniden yazma
ctusch

46

Yukarıdaki popüler cevaba ek olarak, Windows sistemleri için birkaç not eklemek istiyorum. Komuta

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • herhangi bir değişiklik yapmadan mükemmel çalışır ! Bu nedenle, siz olmamalıdır kullanmak Remove-Item, delya da yerine başka bir şey rm -rf.

  • Bir dosya veya dizine bir yol belirtmeniz gerekiyorsa, aşağıdaki gibi eğik çizgiler kullanın./path/to/node_modules


Dizin bir içeriyorsa, bu Windows üzerinde çalışmaz. (nokta) olarak adlandırılır.
Corneliu Serediuc

4
Ve çözümü buldum. Rm komutu için şu şekilde çift ters virgül kullanın: "rm -rf node.modules".
Corneliu Serediuc

23

Bulduğum en iyi ve en doğru yöntem bfg.jar dosyasını indirmekti: https://rtyley.github.io/bfg-repo-cleaner/

Ardından komutları çalıştırın:

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

Dosyaları silmek istiyorsanız, bunun yerine dosyaları sil seçeneğini kullanın:

java -jar bfg.jar --delete-files *.pyc

1
çok kolay :) sadece belirli bir klasörün kaldırıldığından emin olmak istiyorsanız, bu yardımcı olacaktır: stackoverflow.com/questions/21142986/…
emjay

9

Bu kadar güncel cevabı olduğunu göründüğü değil kullanmak filter-branch, doğrudan (en Git de kendisi artık önermez) ve erteleme harici alete işin söyledi. Özellikle, git-filter-repo önerilmektedir. Bu aracın yazarı, doğrudan kullanmanın neden sorunlara yol açabileceğine ilişkin argümanlar sağlarfilter-branch .

dirGeçmişten kaldırmak için yukarıdaki çok satırlı komut dosyalarının çoğu şu şekilde yeniden yazılabilir:

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

Araç görünüşe göre bundan daha güçlü. Filtreleri yazar, e-posta, refname ve daha fazlasına göre uygulayabilirsiniz ( tam sayfa burada ). Ayrıca hızlıdır . Kurulum kolaydır - çeşitli formatlarda dağıtılır .


Güzel bir araç! Ubuntu 20.04 üzerinde iyi çalışır, sadece pip3 install git-filter-repostdlib olduğundan ve herhangi bir bağımlılık yüklemediğinden yapabilirsiniz. Ubuntu 18'de Error: need a version of git whose diff-tree command has the --combined-all-paths optiondocker run -ti ubuntu:20.04
distro'nun

7

Test ettikten sonra yorumlara (kopyala yapıştır çözümü için) komutları ekleyerek kopyala ve yapıştır tarifini tamamlayın:

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

Bundan sonra, "node_modules /" satırını .gitignore'dan kaldırabilirsiniz.


Neden sonra çıkarıyoruz node_modulesgelen .gitignore? Böylece yanlışlıkla tekrar işlenebilsinler ??
Adamski

1
Gitignore'dan kaldırılmaz, gitignore'a eklenir. İşlem mesajı "gitignore" değil, "git geçmişi" diyor :)
Danny Tuppeny

ancak açıklama daha sonra kaldırabilirsiniz söylüyor node_modulesdan .gitignore.
zavr

7

Windows kullanıcısı için, başka bir yedekleme zaten varsa komutu zorlamak için Ayrıca eklendi "yerine kullanmayı unutmayın .'-f

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

3

Windows'ta git kullanarak eski C # projelerinden depo ve obj klasörlerini kaldırdım. Dikkatli ol

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

Git kurulum klasöründeki usr / bin klasörünü silerek git kurulumunun bütünlüğünü yok eder.

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.