Come rimuovere / eliminare un file di grandi dimensioni dalla cronologia di commit nel repository Git?


708

Occasionalmente ho lasciato cadere un DVD-rip in un progetto di un sito Web, poi con noncuranza git commit -a -m ...e, zap, il repository è stato gonfiato da 2,2 concerti. La prossima volta ho apportato alcune modifiche, cancellato il file video e eseguito il commit di tutto, ma il file compresso è ancora presente nel repository, nella cronologia.

So che posso iniziare i rami da quei commit e rifare un ramo su un altro. Ma cosa devo fare per unire i 2 commit in modo che il file di grandi dimensioni non sia stato visualizzato nella cronologia e sia stato pulito nella procedura di garbage collection?


9
Questo articolo dovrebbe aiutarti help.github.com/removing-sensitive-data
MBO


1
Nota che se il tuo file di grandi dimensioni si trova in un sottodir, dovrai specificare il percorso relativo completo.
Johan


Molte risposte qui sotto suggeriscono che BFG è più facile di git filter-branch, ma ho trovato il contrario per essere vero.
2540625,

Risposte:


605

Usa BFG Repo-Cleaner , un'alternativa più semplice e veloce a quella git-filter-branchappositamente progettata per rimuovere file indesiderati dalla cronologia di Git.

Seguire attentamente le istruzioni per l' uso , la parte fondamentale è proprio questa:

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

Qualsiasi file di dimensioni superiori a 100 MB (che non sono nel tuo ultimo commit) verrà rimosso dalla cronologia del tuo repository Git. È quindi possibile utilizzare git gcper pulire i dati morti:

$ git gc --prune=now --aggressive

Il GGG è in genere almeno 10-50 volte più veloce della corsa git-filter-branche generalmente più facile da usare.

Informativa completa: sono l'autore del Repo-Cleaner di BFG.


4
@tony Vale la pena ripetere l'intera procedura di clonazione e cancellazione per vedere se il messaggio che ti chiede di estrarre si ripresenta, ma è quasi certamente perché il tuo server remoto è configurato per rifiutare gli aggiornamenti non rapidi (cioè è configurato per fermarti di perdere la storia - che è esattamente quello che vuoi fare). È necessario modificare tale impostazione sul telecomando o, in caso contrario, trasferire la cronologia repository aggiornata a un repository vuoto nuovo di zecca.
Roberto Tyley,

1
@RobertoTyley Grazie. L'ho provato 3 volte diverse e tutte sono risultate con lo stesso messaggio. Quindi sto anche pensando che hai ragione sul server remoto configurato per rifiutare gli aggiornamenti non rapidi. Prenderò in considerazione il semplice invio del repository aggiornato a un repository nuovo di zecca. Grazie!
Tony,

7
@RobertoTyley Perfetto, mi risparmi tempo, grazie mille. A proposito, forse dovrebbe fare git push --forcedopo i tuoi passaggi, altrimenti il ​​repository remoto non è ancora cambiato.
li2

3
+1 all'aggiunta git push --force. Vale anche la pena notare: il push forzato potrebbe non essere consentito dal telecomando (per impostazione predefinita gitlab.com non ha dovuto "annullare la protezione" del ramo).
MatrixManAtYrService

25
Penso che il gergo di Trump che l'output degli strumenti sia un po 'troppo.
Chris,

564

Quello che vuoi fare è altamente distruttivo se hai pubblicato la cronologia per altri sviluppatori. Consulta la sezione "Ripristino dal rebase upstream" nella git rebasedocumentazione per i passaggi necessari dopo aver riparato la cronologia.

Hai almeno due opzioni: git filter-branche un rebase interattivo, entrambi spiegati di seguito.

utilizzando git filter-branch

Ho avuto un problema simile con ingombranti dati di test binari da un'importazione Subversion e ho scritto sulla rimozione di dati da un repository git .

Dì che la tua cronologia git è:

$ 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

Si noti che git lolaè un alias non standard ma molto utile. Con lo --name-statusswitch, possiamo vedere le modifiche dell'albero associate ad ogni commit.

Nel commit "Careless" (il cui nome dell'oggetto SHA1 è ce36c98) il file oops.isoè il DVD-rip aggiunto per errore e rimosso nel commit successivo, cb14efd. Utilizzando la tecnica descritta nel suddetto post di blog, il comando da eseguire è:

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

Opzioni:

  • --prune-emptyrimuove i commit che diventano vuoti ( cioè non cambiano l'albero) come risultato dell'operazione di filtro. Nel caso tipico, questa opzione produce una cronologia più pulita.
  • -dnomina una directory temporanea che non esiste ancora da utilizzare per creare la cronologia filtrata. Se si esegue su una moderna distribuzione Linux, la specifica di un albero /dev/shmcomporterà un'esecuzione più rapida .
  • --index-filterè l'evento principale e viene eseguito contro l'indice in ogni passaggio della cronologia. Si desidera rimuovere oops.isoovunque si trovi, ma non è presente in tutti i commit. Il comando git rm --cached -f --ignore-unmatch oops.isocancella il DVD-rip quando è presente e non fallisce altrimenti.
  • --tag-name-filterdescrive come riscrivere i nomi dei tag. Un filtro di catè l'operazione di identità. Il tuo repository, come nell'esempio sopra, potrebbe non avere alcun tag, ma ho incluso questa opzione per la massima generalità.
  • -- specifica la fine delle opzioni a git filter-branch
  • --alldi seguito --è una scorciatoia per tutti i riferimenti. Il tuo repository, come l'esempio sopra, può avere solo un ref (master), ma ho incluso questa opzione per la generalità completa.

Dopo un po 'di agitazione, la storia è ora:

$ 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

Si noti che il nuovo commit "Careless" aggiunge solo other.htmle che il commit "Remove DVD-rip" non si trova più nel ramo master. La filiale etichettata refs/original/refs/heads/mastercontiene i tuoi commit originali nel caso in cui tu abbia commesso un errore. Per rimuoverlo, seguire i passaggi in "Elenco di controllo per la riduzione di un repository".

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

Per un'alternativa più semplice, clonare il repository per scartare i bit indesiderati.

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

L'uso di un file:///...URL clone copia gli oggetti anziché solo la creazione di hardlink.

Ora la tua storia è:

$ 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

I nomi degli oggetti SHA1 per i primi due commit ("Indice" e "Pagina di amministrazione") sono rimasti invariati perché l'operazione di filtro non ha modificato tali commit. "Careless" ha perso oops.isoe "Login page" ha ottenuto un nuovo genitore, quindi i suoi SHA1 sono cambiati.

Rebase interattivo

Con una storia di:

$ 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

vuoi rimuovere oops.isoda “Careless” come se non lo avessi mai aggiunto, e quindi “Remove DVD-rip” è inutile per te. Pertanto, il nostro piano per entrare in un rebase interattivo è quello di mantenere la "Pagina di amministrazione", modificare "Careless" e scartare "Rimuovi DVD-rip".

L'esecuzione $ git rebase -i 5af4522avvia un editor con i seguenti contenuti.

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

In esecuzione del nostro piano, lo modifichiamo in

edit ce36c98 Careless
pick f772d66 Login page

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

Cioè, eliminiamo la riga con "Rimuovi DVD-rip" e cambiamo l'operazione su "Careless" per essere editpiuttosto che pick.

Salvare l'uscita dall'editor ci lascia al prompt dei comandi con il seguente messaggio.

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

Come ci dice il messaggio, siamo impegnati nel commit "Careless" che vogliamo modificare, quindi eseguiamo due comandi.

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

Il primo rimuove il file offensivo dall'indice. Il secondo modifica o modifica "Careless" come indice aggiornato e -C HEADindica a git di riutilizzare il vecchio messaggio di commit. Infine, git rebase --continueprocede con il resto dell'operazione rebase.

Questo dà una storia di:

$ 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

che è quello che vuoi


4
Perché non riesco a spingere quando utilizzo git filter-branch, non riesco a inviare alcuni ref a 'git@bitbucket.org: product / myproject.git' Per impedirti di perdere la cronologia, gli aggiornamenti non rapidi sono stati rifiutati Unisci il telecomando cambia prima di spingere di nuovo.
Agung Prasetyo,

11
Aggiungi l' opzione -f(o --force) al tuo git pushcomando: “Di solito, il comando rifiuta di aggiornare un riferimento remoto che non è un antenato del riferimento locale usato per sovrascriverlo. Questo flag disabilita il controllo. Ciò può causare la perdita di commit del repository remoto; usalo con cura. "
Greg Bacon,

5
Questa è una risposta meravigliosamente approfondita che spiega l'uso di git-filter-branch per rimuovere file di grandi dimensioni indesiderati dalla storia, ma vale la pena notare che da quando Greg ha scritto la sua risposta, The BFG Repo-Cleaner è stato rilasciato, che è spesso più veloce e più facile da usa - vedi la mia risposta per i dettagli.
Roberto Tyley,

1
Dopo aver eseguito una delle procedure sopra, il repository remoto (su GitHub) NON elimina il file di grandi dimensioni. Lo fa solo il locale. Forzo push e nada. Cosa mi sto perdendo?
azatar

1
questo funziona anche su dirs. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
rynop,

198

Perché non usare questo comando semplice ma potente?

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

L' --tree-filteropzione esegue il comando specificato dopo ogni checkout del progetto e quindi raccomanda i risultati. In questo caso, rimuovi un file chiamato DVD-rip da ogni istantanea, che esista o meno.

Se sai quale commit ha introdotto l'enorme file (diciamo 35dsa2), puoi sostituire HEAD con 35dsa2..HEAD per evitare di riscrivere troppa cronologia, evitando così commit divergenti se non l'hai ancora fatto. Questo commento per gentile concessione di @ alpha_989 sembra troppo importante per lasciarlo qui.

Vedere questo link .


3
Questa è una buona soluzione! Ho creato un gist che ha uno script Python per elencare i file e il gd cmd che eliminerà il file che vuoi pulire gist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata

5
Molto meglio di bfg. Non sono stato in grado di pulire il file da un git con bfg, ma questo comando ha aiutato
podarok

4
Questo è fantastico Solo una nota per gli altri che dovrai farlo per ramo se il file di grandi dimensioni si trova in più rami.
James,

2
Su Windows ho ottenuto fatal: bad revision 'rm', che ho risolto utilizzando "invece di '. Comando generale:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
marcotama,

2
Se conosci la posizione in commitcui inserisci il file (ad esempio 35dsa2), puoi sostituirlo HEADcon 35dsa2..HEAD. tree-filterè molto più lento di index-filtercosì non tenterà di verificare tutti i commit e riscriverli. se usi HEAD, proverà a farlo.
alpha_989,

86

(La migliore risposta che ho visto a questo problema è: https://stackoverflow.com/a/42544963/714112 , copiata qui poiché questa discussione appare alta nelle classifiche di ricerca di Google ma l'altra no)

🚀 Una fodera incredibilmente veloce conchiglia 🚀

Questo script di shell visualizza tutti gli oggetti BLOB nel repository, ordinati dal più piccolo al più grande.

Per il mio repository di esempio, ha funzionato circa 100 volte più velocemente degli altri trovati qui.
Sul mio fidato sistema Athlon II X4, gestisce il repository del kernel Linux con i suoi 5.622.155 oggetti in poco più di un minuto .

Lo script di base

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

Quando esegui il codice sopra, otterrai un output leggibile come questo:

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

🚀 Rimozione rapida dei file 🚀

Supponiamo che tu voglia rimuovere i file ae bda ogni commit raggiungibile da HEAD, puoi usare questo comando:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD

3
Se il tuo repository ha dei tag, probabilmente vorrai anche aggiungere il flag --tag-name-filter catper ricodificare i nuovi commit corrispondenti man mano che vengono riscritti, ovvero git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD(vedi questa risposta correlata )
naitsirhc

3
Le istruzioni per Mac e alcune altre informazioni compaiono nel post originale collegato
nruth

3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADordine di lavoro a destra della mazza
eleijonmarck

la mia risposta preferita. una leggera modifica da usare su mac os (usando i comandi 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

bel copione con la rev-list ma non ha funzionato per me come alias, hai idea di come farlo?
Robin Manoli,

47

Dopo aver provato praticamente ogni risposta in SO, ho finalmente trovato questo gioiello che ha rimosso e cancellato rapidamente i file di grandi dimensioni nel mio repository e mi ha permesso di sincronizzare di nuovo: http://www.zyxware.com/articles/4027/how-to-delete lime-permanente-da-remote-git-repository-your-local-e

CD nella cartella di lavoro locale ed eseguire il comando seguente:

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

sostituire FOLDERNAME con il file o la cartella che si desidera rimuovere dal repository git specificato.

Al termine, eseguire i seguenti comandi per ripulire il repository locale:

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

Ora invia tutte le modifiche al repository remoto:

git push --all --force

Questo pulirà il repository remoto.


Ha funzionato benissimo per me.
Ramon Vasconcelos,

3
Questo ha funzionato anche per me. Elimina una cartella specifica (nel mio caso, una che conteneva file troppo grandi o un repository Github) sul repository, ma lo mantiene sul file system locale nel caso esista.
skizzo,

Ha funzionato per me! non è rimasta alcuna storia che sia potenzialmente confusa (se qualcuno dove clonare in questo momento), assicurati di avere un piano per aggiornare eventuali collegamenti interrotti, dipendenze, ecc.
Ruoho Ruotsi,

38

Questi comandi hanno funzionato nel mio caso:

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

È leggermente diverso dalle versioni precedenti.

Per coloro che hanno bisogno di spingerlo su github / bitbucket (l'ho provato solo con 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

4
In cosa differisce da quanto sopra, perché è meglio?
Andy Hayden,

1
Per qualche ragione, nel mio caso la versione di mkljun non riduce lo spazio git, ho già rimosso i file dall'indice usando git rm --cached files. La proposta di Greg Bacon è più completa, e abbastanza simile a questa miniera, ma ha perso l'indice --force per i casi in cui stai usando il filtro-ramo per più volte, e ha scritto così tante informazioni, che la mia versione è come riprendere di esso.
Kostanos,

1
Questo mi ha davvero aiutato, ma dovevo usare l' -fopzione non solo -rfqui git rm --cached -rf --ignore-unmatch oops.isoinvece git rm --cached -r --ignore-unmatch oops.isoche per @ lfender6445 di seguito
drstevok,

10

Basta notare che questo comando può essere molto distruttivo. Se più persone stanno lavorando al repository, dovranno tirare il nuovo albero. I tre comandi centrali non sono necessari se il tuo obiettivo NON è ridurre le dimensioni. Perché il ramo del filtro crea un backup del file rimosso e può rimanere lì per molto tempo.

$ 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

11
NON eseguire questi comandi a meno che non si desideri creare un dolore immenso per te stesso. Ha cancellato molti dei miei file di codice sorgente originali. Presumo che eliminerebbe alcuni file di grandi dimensioni dalla mia cronologia di commit in GIT (secondo la domanda originale), tuttavia, penso che questo comando sia progettato per eliminare definitivamente i file dall'albero del codice sorgente originale (grande differenza!). Il mio sistema: Windows, VS2012, Git Source Control Provider.
Contango,

2
Ho usato questo comando: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allinvece del primo dal tuo codice
Kostanos,


8

Se sai che il commit è stato recente invece di passare attraverso l'intero albero, procedi come segue: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD


7

Mi sono imbattuto in questo con un account bitbucket, dove avevo accidentalmente archiviato enormi backup * .jpa del mio sito.

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-DIRECTORYcon la cartella in questione per riscrivere completamente la cronologia ( compresi i tag ).

fonte: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/


1
Questa risposta mi ha aiutato, tranne per il fatto che la sceneggiatura nella risposta ha un leggero problema e non cerca in tutti i rami da me. Ma il comando nel link lo ha fatto perfettamente.
Ali B,

5

Questo lo rimuoverà dalla tua cronologia

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all

Questo ha funzionato per me grazie !!
Sonja Brits,

Questo funziona nel mio caso. Lo eseguo sul tuo ramo principale.
S. Domeng,

4

Fondamentalmente ho fatto quello che era su questa risposta: https://stackoverflow.com/a/11032521/1286423

(per la cronologia, lo copierò e incollerò qui)

$ 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

Non ha funzionato, perché mi piace rinominare e spostare molto le cose. Quindi alcuni file di grandi dimensioni si trovavano in cartelle che sono state rinominate e penso che gc non sia riuscito a eliminare il riferimento a quei file a causa del riferimento negli treeoggetti che puntano a quel file. La mia soluzione definitiva per ucciderlo davvero era:

# 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

Il mio repository (il .git) è cambiato da 32 MB a 388 KB, che nemmeno il ramo filtro poteva pulire.


4

git filter-branchè un potente comando che puoi usare per eliminare un grosso file dalla cronologia dei commit. Il file rimarrà per un po 'e Git lo rimuoverà nella prossima garbage collection. Di seguito è riportato il processo completo dall'eliminazione dei file dalla cronologia di commit . Per motivi di sicurezza, il processo seguente esegue prima i comandi su un nuovo ramo. Se il risultato è quello che ti serviva, ripristinalo sul ramo che desideri effettivamente modificare.

# 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

2

Usa Git Extensions , è uno strumento UI. Ha un plugin chiamato "Trova file di grandi dimensioni" che trova i file lage nei repository e consente di rimuoverli in modo permanente.

Non usare 'git filter-branch' prima di usare questo strumento, poiché non sarà in grado di trovare i file rimossi da 'filter-branch' (Anche se 'filter-branch' non rimuove completamente i file dai file del pacchetto repository) .


Questo metodo è troppo lento per i repository di grandi dimensioni. Ci sono voluti più di un'ora per elencare i file di grandi dimensioni. Quindi quando vado a cancellare i file, dopo un'ora è solo 1/3 del processo di elaborazione del primo file che voglio eliminare.
kristianp,

Sì, è lento, ma il lavoro ... Sai qualcosa di più veloce?
Nir

1
Non l'ho usato, ma BFG Repo-Cleaner, come da un'altra risposta in questa pagina.
kristianp,

2

Puoi farlo usando il branch filtercomando:

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


2

Ci sono risposte molto buone in questo thread, ma nel frattempo molti di loro sono obsoleti. L'uso git-filter-branchnon è più raccomandato, perché è difficile da usare e terribilmente lento su grandi repository.

git-filter-repo è molto più veloce e più semplice da usare.

git-filter-repoè uno script Python, disponibile su github: https://github.com/newren/git-filter-repo .

È necessario solo un file: lo script Python3 git-filter-repo. Copiarlo in un percorso incluso nella variabile PATH. Su Windows potrebbe essere necessario modificare la prima riga dello script (consultare INSTALL.md). È necessario che Python3 sia installato sul tuo sistema, ma questo non è un grosso problema.

Per prima cosa puoi correre

git filter-repo --analyze

Questo ti aiuta a determinare cosa fare dopo.

Puoi eliminare il tuo file DVD-rip ovunque:

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

Il filtro repository è veramente veloce. Un'attività che ha impiegato circa 9 ore sul mio computer per filtro-ramo, è stata completata in 4 minuti con filtro-repo. Puoi fare molte altre cose carine con filter-repo. Fare riferimento alla documentazione per questo.

Avviso: eseguire questa operazione su una copia del repository. Molte azioni di filtro repository non possono essere annullate. filter-repo cambierà gli hash di commit di tutti i commit modificati (ovviamente) e tutti i loro discendenti fino all'ultimo commit!


1

Quando si verifica questo problema, git rmnon sarà sufficiente, poiché git ricorda che il file esisteva una volta nella nostra storia e quindi manterrà un riferimento ad esso.

A peggiorare le cose, neanche il rifacimento non è facile, perché qualsiasi riferimento al BLOB impedirà a Git Garbage Collector di ripulire lo spazio. Ciò include riferimenti remoti e riferimenti di reflog.

Ho messo insieme git forget-blobun piccolo script che tenta di rimuovere tutti questi riferimenti e quindi utilizza git filter-branch per riscrivere ogni commit nel ramo.

Una volta che il tuo BLOB è completamente privo di riferimenti, git gcti libererai di esso

L'utilizzo è piuttosto semplice git forget-blob file-to-forget. Puoi ottenere maggiori informazioni qui

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

L'ho messo insieme grazie alle risposte di Stack Overflow e ad alcuni post di blog. Crediti a loro!


dovresti ottenerlo in homebrew
Cameron E

0

Oltre a git filter-branch(soluzione git lenta ma pura) e BFG (più facile e molto performante), c'è anche un altro strumento per filtrare con buone prestazioni:

https://github.com/xoofx/git-rocket-filter

Dalla sua descrizione:

Lo scopo di git-rocket-filter è simile al comando git-filter-branchmentre fornisce le seguenti caratteristiche uniche:

  • Riscrittura rapida di commit e alberi (per un ordine da x10 a x100).
  • Supporto incorporato sia per la lista bianca con --keep (mantiene file o directory) sia per la lista nera con opzioni --remove.
  • Uso di .gitignore come modello per il filtraggio degli alberi
  • Scripting C # rapido e semplice sia per il filtro di commit che per il filtro ad albero
  • Supporto per gli script nel filtro degli alberi per modello di file / directory
  • Elimina automaticamente i commit vuoti / invariati, inclusi i commit di unione
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.