Rimuovi la cartella e il suo contenuto dalla cronologia di git / GitHub


318

Stavo lavorando su un repository sul mio account GitHub e questo è un problema su cui mi sono imbattuto.

  • Progetto Node.js con una cartella con alcuni pacchetti npm installati
  • I pacchetti erano nella node_modulescartella
  • Aggiunta quella cartella al repository git e trasferito il codice su github (non pensavo alla parte npm in quel momento)
  • Realizzato che non hai davvero bisogno che quella cartella faccia parte del codice
  • Ho eliminato quella cartella, l'ho spinta

In quel caso, la dimensione del repository git totale era di circa 6 MB, mentre il codice effettivo (tutti tranne quella cartella) era solo di circa 300 KB .

Ora quello che sto cercando alla fine è un modo per sbarazzarmi dei dettagli di quella cartella del pacchetto dalla cronologia di git, quindi se qualcuno lo clona, ​​non devono scaricare 6 MB di storia in cui gli unici file reali che otterranno a partire dall'ultimo commit sarebbe 300 KB.

Ho cercato possibili soluzioni per questo e ho provato questi 2 metodi

Sembrava che il Gist funzionasse dove dopo aver eseguito la sceneggiatura, mostrava che si era sbarazzato di quella cartella e che poi mostrava che erano stati modificati 50 diversi commit. Ma non mi ha permesso di spingere quel codice. Quando ho provato a spingerlo, ha detto Branch up to datema ha mostrato che 50 commit sono stati modificati su a git status. Gli altri 2 metodi non hanno aiutato neanche.

Ora, anche se ha dimostrato di essersi sbarazzato della cronologia di quella cartella, quando ho controllato le dimensioni di quel repository sul mio localhost, era ancora circa 6 MB. (Ho anche eliminato la refs/originalcartella ma non ho visto la modifica delle dimensioni del repository).

Quello che sto cercando di chiarire è che se c'è un modo per sbarazzarsi non solo della cronologia del commit (che è l'unica cosa che penso sia accaduta), ma anche di quei file che git continua a supporre che si voglia eseguire il rollback.

Diciamo che una soluzione è presentata per questo e viene applicata sul mio localhost ma non può essere riprodotta in quel repository GitHub, è possibile clonare quel repository, eseguire il rollback al primo commit eseguire il trucco e spingerlo (o significa che git lo farà hai ancora una storia di tutti questi commit? - aka. 6MB).

Il mio obiettivo finale qui è fondamentalmente trovare il modo migliore per sbarazzarsi del contenuto della cartella da git in modo che un utente non debba scaricare roba da 6 MB e possibilmente avere gli altri commit che non hanno mai toccato la cartella dei moduli (è carino molti di loro) nella storia di Git.

Come posso fare questo?


3
Se una delle risposte di seguito ha risolto il tuo problema, forse dovresti considerare di accettarne una come risposta alla tua domanda. meta.stackexchange.com/questions/5234/…
starbeamrainbowlabs

La risposta migliore è: stackoverflow.com/a/32886427/5973334
Kuzeko

Risposte:


556

Se sei qui per copiare e incollare il codice:

Questo è un esempio che rimuove node_modulesdalla storia

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

Cosa fa effettivamente Git:

La prima riga scorre attraverso tutti i riferimenti sullo stesso albero ( --tree-filter) di HEAD (il ramo corrente), eseguendo il comando rm -rf node_modules. Questo comando elimina la cartella node_modules ( -r, senza -r, rmnon eliminerà le cartelle), senza che sia stato richiesto all'utente ( -f). L'aggiunta --prune-emptycancella i commit inutili (non cambiando nulla) in modo ricorsivo.

La seconda riga elimina il riferimento a quel vecchio ramo.

Il resto dei comandi è relativamente semplice.


3
Solo una nota a margine: ero solito git count-objects -vverificare se i file erano stati effettivamente rimossi, ma le dimensioni del repository rimangono le stesse fino a quando non ho clonato nuovamente il repository. Git mantiene una copia di tutti i file originali che penso.
Davide Icardi,

4
Con un git non antico, questo dovrebbe probabilmente leggere --force-with-lease, no --force.
Griwes,

4
Nessuno di questi comandi funziona su Windows. O almeno non Windows 10, inserisci il sistema operativo su cui funziona "taglia e incolla"
David

3
Per gli utenti di Windows 10, funziona perfettamente con Bash per Windows (ho usato Ubuntu)
Andrej Kyselica,

3
L'ho provato con Windows Shell e Git Bash e non ha funzionato. Primo comando passato, secondo comando fallito!
Mohy Eldeen,

240

Trovo che l' --tree-filteropzione utilizzata in altre risposte possa essere molto lenta, specialmente su repository più grandi con molti commit.

Ecco il metodo che uso per rimuovere completamente una directory dalla cronologia di git usando l' --index-filteropzione, che funziona molto più velocemente:

# 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

È possibile verificare le dimensioni del repository prima e dopo il gccon:

git count-objects -vH

3
potresti spiegare perché questo è molto più veloce?
Knocte,

7
@knocte: dai documenti ( git-scm.com/docs/git-filter-branch ). "--index-filter: ... è simile al filtro dell'albero ma non controlla l'albero, il che lo rende molto più veloce"
Lee Netherton,

23
Perché questa non è la risposta accettata? È così accurato.
Fisico pazzo,

2
Se lo fai in Windows, hai bisogno di virgolette doppie invece di virgolette singole.
Kris Morness,

12
Passando --quieta quanto git rmsopra accelerato la mia riscrittura almeno per fattore 4.
ctusch

46

Oltre alla popolare risposta sopra, vorrei aggiungere alcune note per i sistemi Windows . Il comando

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • funziona perfettamente senza alcuna modifica! Pertanto, non si deve utilizzare Remove-Item, delo qualsiasi altra cosa al posto di rm -rf.

  • Se è necessario specificare un percorso per un file o una directory, utilizzare le barre come./path/to/node_modules


Questo non funzionerà su Windows se la directory contiene a. (punto) nel nome.
Corneliu Serediuc,

4
E ho trovato la soluzione. Utilizzare le virgolette doppie per il comando rm in questo modo: "rm -rf node.modules".
Corneliu Serediuc,

23

Il metodo migliore e più accurato che ho trovato è stato quello di scaricare il file bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

Quindi eseguire i comandi:

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

Se si desidera eliminare i file, utilizzare invece l'opzione di eliminazione dei file:

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

1
molto semplice :) se vuoi assicurarti che venga rimossa solo una cartella specifica, questo ti aiuterà: stackoverflow.com/questions/21142986/…
emjay

9

Sembra che la risposta aggiornata a questo non sia quella di utilizzare filter-branchdirettamente (almeno Git stesso non lo raccomanda più) e rinviare quel lavoro a uno strumento esterno. In particolare, si consiglia attualmente git-filter-repo . L'autore di questo strumento fornisce argomenti sul perché l'uso filter-branchdiretto può portare a problemi.

La maggior parte degli script multilinea sopra per rimuovere dirdalla cronologia potrebbe essere riscritta come:

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

Lo strumento è più potente di quello, a quanto pare. Puoi applicare i filtri per autore, e-mail, refname e altro ( pagina man completa qui ). Inoltre, è veloce . L'installazione è semplice: è distribuita in vari formati .


Strumento carino! Funziona bene su Ubuntu 20.04, puoi solo pip3 install git-filter-repoperché è solo stdlib e non installa alcuna dipendenza. Su Ubuntu 18 è incompatibile con la versione git della distro Error: need a version of git whose diff-tree command has the --combined-all-paths option, ma è abbastanza facile eseguirlo su undocker run -ti ubuntu:20.04
kubanczyk

7

Completa la ricetta copia e incolla, semplicemente aggiungendo i comandi nei commenti (per la soluzione copia-incolla), dopo averli testati:

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

Successivamente, è possibile rimuovere la riga "node_modules /" da .gitignore


Perché dovresti rimuovere node_modulesda .gitignore? In modo che possano essere commessi di nuovo accidentalmente ??
Adamski,

1
Non viene rimosso da gitignore, viene aggiunto a gitignore. Il messaggio di commit dice "cronologia git", non "gitignore" :)
Danny Tuppeny

ma il commento dice che è possibile rimuovere node_modulesda .gitignore.
zavr,

7

Per gli utenti di Windows, tenere presente che al "posto di ' È stato aggiunto anche -fper forzare il comando se è già presente un altro backup.

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

Ho rimosso le cartelle bin e obj dai vecchi progetti C # usando git su windows. Stai attento con

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

Distrugge l'integrità dell'installazione di git eliminando la cartella usr / bin nella cartella di installazione di git.

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.