Come far sì che Git “dimentichi” di un file che è stato rintracciato ma ora è in .gitignore?

C’è un file che è stato seguito da git , ma ora il file si trova nell’elenco .gitignore .

Tuttavia, quel file continua a mostrarsi in git status dopo che è stato modificato. Come costringere la git a dimenticarsene completamente?

.gitignore impedirà l’aggiunta di file non tracciati (senza un add -f ) all’insieme di file tracciati da git, tuttavia git continuerà a tracciare tutti i file che sono già stati rintracciati.

Per interrompere il tracciamento di un file è necessario rimuoverlo dall’indice. Questo può essere ottenuto con questo comando.

 git rm --cached  

La rimozione del file dalla revisione principale avverrà al prossimo commit.

La serie di comandi di seguito rimuoverà tutti gli elementi dall’indice Git (non dalla directory di lavoro o dal repository locale), quindi aggiorna l’indice Git, rispettando le ignorazioni git. PS. Indice = Cache

Primo:

 git rm -r --cached . git add . 

Poi:

 git commit -am "Remove ignored files" 

git update-index fa il lavoro per me:

 git update-index --assume-unchanged  

Nota: questa soluzione è in realtà indipendente su .gitignore poiché gitignore è solo per i file non tracciati.

modifica: poiché questa risposta è stata pubblicata, è stata creata una nuova opzione che dovrebbe essere preferita. Dovresti usare --skip-worktree che è per i file di tracciamento modificati che l’utente non vuole più eseguire il commit e mantenere --assume-unchanged per le prestazioni per impedire a git di controllare lo stato dei grandi file tracciati. Vedi https://stackoverflow.com/a/13631525/717372 per maggiori dettagli …

 git ls-files --ignored --exclude-standard -z | xargs -0 git rm --cached git commit -am "Remove ignored files" 

Questo prende l’elenco dei file ignorati e li rimuove dall’indice, quindi impegna le modifiche.

Io uso sempre questo comando per rimuovere quei file non tracciati. Output pulito su una sola riga, in stile Unix:

 git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached 

Elenca tutti i file ignorati, sostituisce invece ogni riga di output con una linea quotata per gestire i percorsi con spazi interni e passa tutto a git rm -r --cached per rimuovere i percorsi / file / dir dall’indice.

Se non è ansible creare un file tracciato perché altre persone potrebbero averne bisogno (avviso, anche se si git rm --cached , quando qualcun altro ottiene questa modifica, i loro file saranno eliminati nel loro filesystem) per favore guarda https: // gist .github.com / 1423106 per i modi in cui le persone hanno lavorato attorno al problema.

spostalo, commetti, quindi spostalo nuovamente. Questo ha funzionato per me in passato. C’è probabilmente un modo ‘gittier’ per realizzare questo.

Cosa non ha funzionato per me

(Sotto Linux), volevo usare i post qui suggerendo i ls-files --ignored --exclude-standard | xargs git rm -r --cached ls-files --ignored --exclude-standard | xargs git rm -r --cached . Tuttavia, (alcuni dei) i file da rimuovere avevano una nuova riga incorporata / LF / \n nei loro nomi. Nessuna delle soluzioni:

 git ls-files --ignored --exclude-standard | xargs -d"\n" git rm --cached git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached 

far fronte a questa situazione (ottenere errori sui file non trovati).

Quindi offro

 git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached 

Questo usa l’argomento -z per ls-files , e l’argomento -0 per xargs per soddisfare in modo sicuro / corretto i caratteri “cattivi” nei nomi dei file.

Nella pagina di manuale git-ls-files (1) , afferma:

Quando l’opzione -z non viene utilizzata, i caratteri TAB, LF e backslash nei nomi di percorso sono rappresentati come \ t, \ n e \\, rispettivamente.

quindi penso che la mia soluzione sia necessaria se i nomi dei file contengono alcuni di questi caratteri.

EDIT: mi è stato chiesto di aggiungere che — come ogni comando git rm — questo deve essere seguito da un commit per rendere permanenti le rimozioni, es. git commit -am "Remove ignored files" .

Ho realizzato questo usando git filter-branch . Il comando esatto che ho usato è stato preso dalla pagina man:

ATTENZIONE : questo cancellerà il file dalla tua intera cronologia

 git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD 

Questo comando ricrea l’intera cronologia del commit, eseguendo git rm prima di ogni commit e quindi eliminerà il file specificato. Non dimenticare di eseguirne il backup prima di eseguire il comando poiché verrà perso.

Usalo quando:

1. Si desidera annullare la traccia di molti file o

2. Hai aggiornato il tuo file gitignore

Link alla fonte: http://www.codeblocq.com/2016/01/Untrack-files-already-added-to-git-repository-based-on-gitignore/

Diciamo che hai già aggiunto / inserito alcuni file nel tuo repository git e li aggiungi al tuo .gitignore; questi file saranno ancora presenti nel tuo indice di repository. Questo articolo vedremo come sbarazzarsi di loro.

Passaggio 1: conferma tutte le modifiche

Prima di procedere, assicurati che tutte le modifiche siano state eseguite, incluso il file .gitignore.

Passaggio 2: rimuovi tutto dal repository

Per cancellare il tuo deposito, usa:

 git rm -r --cached . 
  • rm è il comando remove
  • -r consentirà la rimozione ricorsiva
  • -Cache rimuoverà solo i file dall’indice. I tuoi file saranno ancora lì.

Il comando rm può essere implacabile. Se vuoi provare in anticipo cosa fare, aggiungi il flag -n o --dry-run per testare le cose.

Passaggio 3: aggiungi di nuovo tutto

 git add . 

Passaggio 4: commit

 git commit -m ".gitignore fix" 

Il tuo repository è pulito 🙂

Spingere le modifiche sul telecomando per vedere anche le modifiche effettive.

  1. Aggiorna il tuo file .gitignore – ad esempio, aggiungi una cartella a cui non vuoi tracciare .gitignore .

  2. git rm -r --cached . – Rimuovi tutti i file tracciati, inclusi quelli desiderati e indesiderati. Il tuo codice sarà al sicuro finché avrai salvato localmente.

  3. git add . – Tutti i file verranno aggiunti nuovamente, tranne quelli in .gitignore .


Punta di cappello su @AkiraYamamoto per averci indicato nella giusta direzione.

Penso che forse git non può totalmente dimenticare il file a causa della sua concezione ( sezione “Istantanee, Non differenze” ).

Questo problema è assente, ad esempio, quando si utilizza CVS. CVS memorizza le informazioni come un elenco di modifiche basate su file. Le informazioni per CVS sono un insieme di file e le modifiche apportate a ciascun file nel tempo.

Ma in Git ogni volta che si commette o si salva lo stato del progetto, in pratica si fa una foto di come appaiono tutti i file in quel momento e si memorizza un riferimento a quell’istantanea. Quindi, se hai aggiunto un file una volta, sarà sempre presente in quell’istantanea.

Questi 2 articoli mi sono stati utili:

git assumere-invariato vs skip-worktree e Come ignorare le modifiche nei file tracciati con Git

Basandoci su quanto segue faccio quanto segue, se il file è già tracciato:

 git update-index --skip-worktree  

Da questo momento tutte le modifiche locali in questo file verranno ignorate e non passeranno al remoto. Se il file viene modificato su remoto, si verificherà un conflitto, quando git pull . Stash non funzionerà. Per risolverlo, copia il contenuto del file nel posto sicuro e segui questi passaggi:

 git update-index --no-skip-worktree  git stash git pull 

Il contenuto del file verrà sostituito dal contenuto remoto. Incolla le tue modifiche da un posto sicuro a un file ed esegui di nuovo:

 git update-index --skip-worktree  

Se tutti, chi lavora con il progetto, eseguiranno git update-index --skip-worktree , i problemi con pull dovrebbero essere assenti. Questa soluzione è OK per i file di configurazione, quando ogni sviluppatore ha la propria configurazione di progetto.

Non è molto comodo farlo ogni volta, quando il file è stato modificato sul telecomando, ma può proteggerlo dalla sovrascrittura di contenuti remoti.

Sposta o copia il file in un luogo sicuro, in modo da non perderlo. Quindi git rm il file e commit. Il file verrà comunque visualizzato se si ripristina uno di questi commit precedenti o un altro ramo in cui non è stato rimosso. Tuttavia, in tutti i futuri commit, non vedrai più il file. Se il file è git ignore, puoi spostarlo nuovamente nella cartella e git non lo vedrà.

La risposta di Matt Fear è stata l’IMHO più efficace. Quanto segue è solo uno script PowerShell per quelli in Windows per rimuovere solo i file dal loro repo Git che corrisponde al loro elenco di esclusione.

 # Get files matching exclusionsfrom .gitignore # Excluding comments and empty lines $ignoreFiles = gc .gitignore | ?{$_ -notmatch "#"} | ?{$_ -match "\S"} | % { $ignore = "*" + $_ + "*" (gci -r -i $ignore).FullName } $ignoreFiles = $ignoreFiles| ?{$_ -match "\S"} # Remove each of these file from Git $ignoreFiles | % { git rm $_} git add . 

Il BFG è progettato specificamente per rimuovere dati indesiderati come grandi file o password da repository Git, quindi ha un semplice flag che rimuoverà qualsiasi file storico di grandi dimensioni (non-in-your-current-commit): ‘–strip-blobs- più grande di’

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

Se desideri specificare i file per nome, puoi farlo anche tu:

 $ java -jar bfg.jar --delete-files *.mp4 

Il BFG è 10-1000 volte più veloce di git-filter-branch, e in generale molto più facile da usare: controlla le istruzioni e gli esempi di utilizzo completi per maggiori dettagli.

Fonte: https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html

Se non vuoi usare la CLI e stai lavorando su Windows, una soluzione molto semplice è usare TortoiseGit , ha l’azione “Cancella (mantieni locale)” nel menu che funziona bene.

Mi è piaciuta la risposta di JonBrave, ma ho delle directory di lavoro abbastanza disordinate che mi commettono – mi spaventa un po ‘, quindi ecco cosa ho fatto:

git config – alias.exclude-ignored globale ‘! git ls-files -z –ignored –exclude-standard | xargs -0 git rm -r –cached && git ls-files -z –ignored –exclude-standard | xargs -0 git stage && git stage .gitignore && git commit -m “nuovo gitignore e rimuovi i file ignorati dall’indice” ‘

scomposizione:

 git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached git ls-files -z --ignored --exclude-standard | xargs -0 git stage git stage .gitignore git commit -m "new gitignore and remove ignored files from index" 
  • rimuovi i file ignorati dall’indice
  • stage .gitignore e i file appena rimossi
  • commettere

Questo non è più un problema nell’ultimo git (v2.17.1 al momento della scrittura).

Il .gitignore fine ignora i file rintracciati ma cancellati. Puoi testarlo da solo eseguendo il seguente script. L’ultima dichiarazione git status dovrebbe riportare “nulla da impegnare”.

 # Create empty repo mkdir gitignore-test cd gitignore-test git init # Create a file and commit it echo "hello" > file git add file git commit -m initial # Add the file to gitignore and commit echo "file" > .gitignore git add .gitignore git commit -m gitignore # Remove the file and commit git rm file git commit -m "removed file" # Reintroduce the file and check status. # .gitignore is now respected - status reports "nothing to commit". echo "hello" > file git status 

In caso di DS_Store già impegnato:

 find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch 

Ignorali vicino a:

 echo ".DS_Store" >> ~/.gitignore_global echo "._.DS_Store" >> ~/.gitignore_global echo "**/.DS_Store" >> ~/.gitignore_global echo "**/._.DS_Store" >> ~/.gitignore_global git config --global core.excludesfile ~/.gitignore_global 

Finalmente, fai un commit!