2009-05-30 22 views
20

Ho un file di 33 MB di grandi dimensioni in cui voglio eliminare definitivamente le revisioni più vecchie di quel file, quindi sono conservate solo le ultime versioni di X. Come farlo?git rimuove le revisioni più vecchie di un file

Il mio repository nudo è cresciuto enorme a causa di esso.

Ho provato quanto segue .. ma rimuove il file interamente

git filter-branch --index-filter 'git rm --cached --ignore-unmatch big_manual.txt' HEAD 

Per identificare i file di grandi dimensioni a mio repository che uso git-large-blob by Aristotle Pagaltzis.

+0

Penso che sarebbe d'aiuto se avessi dato qualche informazione in più su questo file e su cosa stai cercando di fare. Si tratterà di un evento unico o pianifichi di eliminare il file e di riscrivere regolarmente la cronologia del repository? Perché stai seguendo il file in git se non hai bisogno di mantenere la sua cronologia? Quanto è grande il tuo repository nudo ed è davvero un problema se è grande? –

+0

è un manuale per il mio programma, sto scrivendo usando Apple Pages (word processor) e include molte immagini. Lo immagazzino in GIT soprattutto per poterlo condividere tra il mio computer fisso e il mio portatile, e così posso annullare nel caso qualcosa vada storto. Il repository è attualmente 450 MB. Esito a lavorare con il file perché so che le dimensioni del repository aumentano .. Invece di ripensare alla mia soluzione di backup ho pensato che sarebbe stato meglio sbarazzarsi delle revisioni più vecchie. Prendo giornalmente un'istantanea completa del repository e la carica, ma la quota del disco è di 3 GB. – neoneye

+0

sì, spero che sia possibile farlo di tanto in tanto. – neoneye

risposta

16

Penso che tu sia sulla strada giusta con il comando git filter-branch che hai provato. Il problema è che non l'hai detto di mantenere il file in qualsiasi commit, quindi viene rimosso da tutti loro. Ora, non penso che ci sia un modo per direttamente dire allo git-filter-branch di saltare qualsiasi commit. Tuttavia, poiché i comandi vengono eseguiti in un contesto di shell, non dovrebbe essere troppo difficile utilizzare la shell per rimuovere tutto tranne l'ultimo numero X di revisioni. Qualcosa di simile a questo:

KEEP=10 I=0 NUM_COMMITS=$(git rev-list master | wc -l) \ 
git filter-branch --index-filter \ 
'if [[ ${I} -lt $((NUM_COMMITS - KEEP)) ]]; then 
    git rm --cached --ignore-unmatch big_manual.txt; 
fi; 
I=$((I + 1))' 

che avrebbe mantenuto big_manual.txt negli ultimi 10 commit.

Detto questo, come ha detto Charles, non sono sicuro che questo sia l'approccio migliore, poiché in effetti stai annullando l'intero punto di VCS eliminando le vecchie versioni.

Hai già provato a ottimizzare il repository git con git-gc e/o git-repack? In caso contrario, potrebbe valere la pena provare.

+1

questa è la soluzione! Ha attraversato tutte le 312 revisioni e scartato le più vecchie revisioni, perfetto. Questo è stato molto istruttivo. Per loop, rev-list .. e chiamando filter-branch senza alcun commit id che sembra non intuitivo (dovrà indagare su come funziona quella magia), ma ha funzionato. Grazie per questo. A volte uso git-gc e fsck, ma non è ancora qualcosa che ho automatizzato. Non parliamo della mia opinione su VCS :-) – neoneye

+1

>> Non parliamo della mia opinione su VCS :-) Abbastanza soddisfacente :) Sono contento che questo ha funzionato per voi. Per quanto riguarda la magia di non specificare una revisione, git-filter-branch chiama internamente git-rev-list per ottenere l'elenco di commit da riscrivere. Passerà "HEAD" a git-rev-list come riferimento predefinito se non ne specifichi uno. Quindi non specificare nulla è lo stesso che specificare "HEAD" (come hai fatto nel tuo esempio). –

+0

Grazie per la sceneggiatura. Ho fatto in un file di script bash e ho trovato che avevo bisogno di regolarlo leggermente ' #!/bin/bash KEEP = 10 I = 0 NUM_COMMITS = $ (git rev-list master | wc -l) \ git filter-branch --index-filter \ 'se [$ {I} -lt $ ((NUM_COMMITS - KEEP))]; then git rm --cached --ignore-unmatch file-to-delete.tar; fi; I = $ ((I + 1)) ' ' –

15

Nota: questa risposta è di circa accorciando la storia di un intero progetto, piuttosto che rimuovere singolo file dalla storia più vecchia quanto la questione era di circa!


Il modo più semplice per ridurre la storia di un intero progettoutilizzando git filter-branch sarebbe quella di utilizzare innesti meccanismo (vedi repository layout documentazione) per accorciare la storia:

$ echo "$commit_id" >> .git/info/grafts 

dove $commit_id è un commit che si desidera essere una radice (first commit) di un nuovo repository. Dai un'occhiata a "git log" o al visualizzatore della cronologia grafica come gitk che la cronologia sembra come vuoi, ed esegui "git filter-branch --all"; l'uso degli innesti è descritto nella documentazione di git-filter-branch.

Oppure è possibile utilizzare clone superficiale utilizzando l'opzione --depth <depth> di git clone.



È possibile fare uso di innesti la rimozione della cronologia parte di un singolo file (quello che è stato originariamente richiesto) utilizzando passaggi descrivono di seguito. Questa soluzione comprende più passaggi di solution proposed by Dan Moulding, ma ognuno dei passaggi è più semplice ed è possibile controllare i passaggi intermedi utilizzando "git log" o il visualizzatore della cronologia grafica.

  1. Innanzitutto, selezionare il punto in cui si desidera rimuovere il file e contrassegnare tali commit creando rami in quei punti. Per esempio, se si desidera avere file di apparire per la prima volta nel commettere f020285b e lo hanno rimosso tutto ciò che gli antenati, segnarlo antenato (supponendo questo è normale, non-merge commit) usando

    $ git branch cleanup f020285b^ 
    
  2. In secondo luogo, rimuovere il file dalla storia che inizia con cleanup (cioè f020285b^) utilizzando git-filter-branch, come illustrato nella sezione "Esempi" di git-filter-branch manpage:

    $ git filter-branch --index-filter 'git rm --cached --ignore-unmatch big_manual.txt' cleanup 
    

    Se si desidera rimuovere anche tutti i commit che aveva cambiato solo rimosso il file è inoltre possibile utilizzare --prune-empty opzione per git-filter-branch.

  3. Avanti, unire una parte riscritto la storia con il resto della storia utilizzando il meccanismo innesti:

    $ echo $(git-rev-parse f020285b) $(git rev-parse cleanup) >> .git/info/grafts 
    

    Quindi è possibile esaminare histry per verificare se è unito in modo corretto.

  4. scorso, fanno innesti permanente (questo renderebbe tutti gli innesti permanenti, ma lascia supporre che qui non si usa innesti altrimenti) utilizzando git-filtro-ramo,

    $ git filter-branch cleanup..HEAD 
    

    e rimuovere gli innesti (come non sono necessari più), e il ramo cleanup

    $ rm .git/info/grafts 
    $ git branch -d cleanup 
    

Nota finale: se si rimuove una parte della storia di alcuni file, è meglio assicurarsi che questo progetto senza file ha senso (e per esempio compila correttamente).

+0

interessante. proverò. – neoneye

+0

sì, il meccanismo degli innesti sembra essere il modo previsto per farlo. Grazie per avermi fatto conoscere questo. Sfortunatamente non ho tempo per sperimentarlo oggi. – neoneye

+0

Il metodo di innesti funziona in alcuni casi, ma si sbarazza della cronologia di tutti i file. In questo caso, neoneye desidera rimuovere solo la cronologia per * alcuni * file. Quindi non sono sicuro che gli innesti sarebbero una soluzione adatta. E il clone superficiale è fuori questione perché i repository superficiali sono storpi (vedi i documenti git-clone per una descrizione dei loro limiti). –

3

Si potrebbe prendere in considerazione l'utilizzo di git submodules. In questo modo è possibile conservare le immagini e altri file di grandi dimensioni in un altro repository git e il repository che contiene i codici sorgente può fare riferimento a una particolare revisione di quell'altro repository.

Ciò consente di mantenere sincronizzate le revisioni del repository, poiché il repository principale contiene un collegamento a una particolare revisione del repository secondario. Ti permetterà anche di rimuovere/rebase le vecchie revisioni nel repository secondario, senza influenzare il repository principale dove è il tuo codice sorgente - le rimozioni delle vecchie revisioni in un repository secondario non rovinano la cronologia del repository principale, perché tu solo aggiorna quella a cui punta il link del repository secondario nel repository principale.

+0

buon punto. Non sapevo dei sottomoduli git. – neoneye

Problemi correlati