Domanda

Ho accidentalmente commesso un file indesiderato (filename.orig durante la risoluzione di un'unione) nel mio repository diversi commit fa, senza che me ne accorgessi fino ad ora.Voglio eliminare completamente il file dalla cronologia del repository.

È possibile riscrivere la cronologia dei cambiamenti in questo modo filename.orig non è mai stato aggiunto al repository in primo luogo?

È stato utile?

Soluzione

Non utilizzare questa ricetta se la tua situazione non è quella descritta nella domanda. Questa ricetta serve per correggere una fusione errata e riprodurre nuovamente i tuoi buoni commit su una fusione fissa.

Sebbene filter-branch farà quello che vuoi, è un comando abbastanza complesso e probabilmente sceglierei di farlo con git rebase. È probabilmente una preferenza personale. rebase può farlo in un singolo comando leggermente più complesso, mentre la soluzione git commit --amend sta eseguendo le operazioni logiche equivalenti un passo alla volta.

Prova la seguente ricetta:

(Nota che in realtà non hai bisogno di un ramo temporaneo, puoi farlo con un 'HEAD distaccato', ma devi prendere nota dell'ID commit generato dal passaggio <=> per fornire al < => comando anziché utilizzare il nome del ramo temporaneo.)

Altri suggerimenti

Introduzione: sono disponibili 5 soluzioni

Il poster originale indica:

  

Ho accidentalmente eseguito il commit di un file indesiderato ... nel mio repository diversi commit    fa ... Voglio eliminare completamente il file dalla cronologia del repository.

     

Lo è    possibile riscrivere la cronologia delle modifiche in modo che filename.orig non sia mai stato    aggiunto al repository in primo luogo?

Esistono molti modi diversi per rimuovere completamente la cronologia di un file git:

  1. Impegni di modifica.
  2. Ripristini hardware (possibilmente più un rebase).
  3. Reimpostazione non interattiva.
  4. Rinnovo interattivo.
  5. Filtraggio dei rami.

Nel caso del poster originale, la modifica del commit non è in realtà un'opzione da solo, dal momento che ha fatto diversi altri impegni in seguito, ma per il bene di completezza, spiegherò anche come farlo, per chiunque altro che si fida desidera modificare il precedente commit.

Nota che tutte queste soluzioni implicano alterare / riscrivere cronologia / commit in un altro modo, quindi chiunque dovrà fare una copia delle vecchie copie lavoro extra per risincronizzare la loro storia con la nuova storia.


Soluzione 1: impegni di modifica

Se hai apportato una modifica accidentale (come l'aggiunta di un file) nel tuo precedente commetti e non vuoi più che esista la cronologia di quel cambiamento puoi semplicemente modificare il commit precedente per rimuovere il file da esso:

git rm <file>
git commit --amend --no-edit

Soluzione 2: hard reset (possibilmente più un rebase)

Come la soluzione n. 1, se vuoi semplicemente sbarazzarti del tuo precedente commit, allora tu hanno anche la possibilità di fare semplicemente un hard reset al suo genitore:

git reset --hard HEAD^

Questo comando ripristinerà il ramo alla precedente 1 st genitore commit.

Tuttavia , se, come il poster originale, hai fatto diversi commit dopo il commit a cui vuoi annullare la modifica, puoi comunque utilizzare i ripristini rigidi modificarlo, ma farlo comporta anche l'uso di un rebase. Ecco i passaggi che puoi utilizzare per modificare un commit più indietro nella storia:


Soluzione 3: Rebase non interattivo

Funzionerà se desideri rimuovere completamente un commit dalla cronologia:


Soluzione 4: basi interattive

Questa soluzione ti permetterà di realizzare le stesse cose delle soluzioni 2 e # 3, ovvero modifica o rimuovi commit più indietro nella cronologia rispetto a quelli eseguiti immediatamente commit precedente, quindi quale soluzione scegli di utilizzare dipende da te. I rebase interattivi non sono adatti per rifare centinaia di commit, per motivi di prestazione, quindi utilizzerei rebases non interattivi o il ramo del filtro soluzione (vedi sotto) in questo tipo di situazioni.

Per iniziare il rebase interattivo, utilizzare quanto segue:

Questo farà tornare indietro la cronologia di commit da git al genitore di impegno che si desidera modificare o rimuovere. Ti presenterà quindi un elenco di rewound si impegna in ordine inverso in qualunque editor git sia impostato (questo è Vim di default):

pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)

Il commit che si desidera modificare o rimuovere sarà in cima a questo elenco. Per rimuoverlo, è sufficiente eliminare la sua riga nell'elenco. Altrimenti, sostituire & Quot; pick & Quot; con quot &; & modifica quot; sulla prima riga st , in questo modo:

edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`

Successivamente, inserisci git rebase --continue. Se hai scelto di rimuovere completamente il commit, quindi tutto ciò che devi fare (oltre alla verifica, vedi il passaggio finale per questa soluzione). Se, d'altra parte, si desidera modificare il commit, quindi git riapplicherà il commit e quindi metterà in pausa il rebase.

Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

A questo punto, è possibile rimuovere il file e modificare il commit, quindi continuare rebase:

git rm <file>
git commit --amend --no-edit
git rebase --continue

Questo è tutto. Come passaggio finale, se hai modificato il commit o rimosso completamente, è sempre una buona idea verificare che nessun altro cambiamento imprevisto sono stati fatti al tuo ramo da diffing con il suo stato prima del rebase:

git diff master@{1}

Soluzione 5: filtrare i rami

Infine, questa soluzione è la migliore se si desidera cancellare completamente tutte le tracce di l'esistenza di un file dalla cronologia e nessuna delle altre soluzioni è all'altezza l'attività.

Ciò rimuoverà <file> da tutti i commit, a partire dal commit root. Se invece vuoi solo riscrivere l'intervallo di commit HEAD~5..HEAD, quindi puoi farlo passalo come argomento aggiuntivo a filter-branch, come sottolineato in questa risposta :

Ancora una volta, dopo che git filter-branch è completo, di solito è una buona idea verificare che non ci sono altri cambiamenti imprevisti diffondendo il tuo ramo con il suo stato precedente prima dell'operazione di filtro:

<*>

Alternativa filtro-ramo: BFG Repo Cleaner

Ho sentito che lo strumento BFG Repo Cleaner funziona più velocemente di --strip-blobs-bigger-than 1M, quindi potresti voler controllare anche questo come opzione. È persino menzionato ufficialmente nella documentazione del ramo di filtro come alternativa praticabile :

  

git-filter-branch ti permette di effettuare riscritture complesse con script shell   della tua cronologia di Git, ma probabilmente non hai bisogno di questa flessibilità se   & # 8217; stai semplicemente rimuovendo i dati indesiderati come file di grandi dimensioni o password.   Per tali operazioni potresti prendere in considerazione Il GGG   Repo-Cleaner , basato su JVM   alternativa a git-filter-branch, in genere almeno 10-50x più veloce per   quei casi d'uso e con caratteristiche abbastanza diverse:

     
      
  • Qualsiasi versione particolare di un file viene ripulita esattamente una volta . Il GGG, a differenza di git-filter-branch, non ti dà la possibilità di gestirlo   un file diverso in base a dove o quando è stato eseguito il commit all'interno del tuo   storia. Questo vincolo offre il vantaggio prestazionale di The   BFG, ed è adatto al compito di pulire i dati errati - non & # 8217; t   cura dove sono i dati errati, li vuoi solo andato .

  •   
  • Per impostazione predefinita, il GGG sfrutta appieno le macchine multi-core, pulendo in parallelo gli alberi dei file di commit. git-filter-branch pulisce   si impegna in sequenza (cioè in un thread singolo), sebbene sia   possibile scrivere filtri che includano il proprio parallelismo, nel file   script eseguiti per ogni commit.

  •   
  • Le opzioni di comando sono molte   più restrittivo del ramo git-filter e dedicato solo a   attività di rimozione di dati indesiderati, ad es. <=>.

  •   

Risorse aggiuntive

  1. Pro Git & # 167; 6.4 Git Tools - Riscrivere la cronologia .
  2. Pagina di manuale git-filter-branch (1) .
  3. Pagina di manuale git-commit (1) .
  4. Pagina del manuale git-reset (1) .
  5. Pagina di manuale git-rebase (1) .
  6. The BFG Repo Cleaner (vedi anche questa risposta del creatore stesso ).

Se non hai commesso nulla da allora, basta git rm il file e git commit --amend.

Se hai

git filter-branch \
--index-filter 'git rm --cached --ignore-unmatch path/to/file/filename.orig' merge-point..HEAD

esaminerà ogni modifica da merge-point a HEAD, eliminerà nomefile.orig e riscriverà la modifica. L'uso di --ignore-unmatch significa che il comando non fallirà se per qualche motivo il nomefile.orig non è presente in una modifica. Questo è il modo consigliato dalla sezione Esempi nella pagina man git-filter-branch .

Nota per utenti Windows: il percorso del file deve utilizzare le barre rovesciate

Questo è il modo migliore:
http://github.com/guides/completely-remove-a -file-from-all-revisions

Assicurati di eseguire prima il backup delle copie dei file.

Modifica

La modifica di Neon è stata purtroppo respinta durante la revisione.
Vedi i post di Neons di seguito, potrebbe contenere informazioni utili!


es. per rimuovere tutti i *.gz file accidentalmente inseriti nel repository git:

$ du -sh .git ==> e.g. 100M
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch *.gz' HEAD
$ git push origin master --force
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --prune=now
$ git gc --aggressive --prune=now

Non ha ancora funzionato per me? (Attualmente sono alla versione 1.7.6.1 di git)

$ du -sh .git ==> e.g. 100M

Non so perché, dato che avevo UN SOLO ramo master. Ad ogni modo, finalmente ho ottenuto il mio repository git veramente ripulito spingendo in un nuovo repository git vuoto e vuoto, ad esempio

$ git init --bare /path/to/newcleanrepo.git
$ git push /path/to/newcleanrepo.git master
$ du -sh /path/to/newcleanrepo.git ==> e.g. 5M 

(sì!)

Quindi l'ho clonato in una nuova directory e ho spostato la sua cartella .git in questa. per es.

$ mv .git ../large_dot_git
$ git clone /path/to/newcleanrepo.git ../tmpdir
$ mv ../tmpdir/.git .
$ du -sh .git ==> e.g. 5M 

(sì! finalmente ripulito!)

Dopo aver verificato che tutto va bene, puoi eliminare le directory ../large_dot_git e ../tmpdir (forse tra un paio di settimane o un mese da adesso, per ogni evenienza ...)

La riscrittura della cronologia di Git richiede la modifica di tutti gli ID di commit interessati, quindi tutti coloro che stanno lavorando al progetto dovranno eliminare le loro vecchie copie del repository e fare un nuovo clone dopo aver pulito la cronologia. Più persone crea inconvenienti, più hai bisogno di una buona ragione per farlo - il tuo file superfluo non sta realmente causando un problema, ma se solo tu stai lavorando al progetto, potresti anche pulire nella storia di Git, se vuoi!

Per semplificare al massimo, ti consiglio di utilizzare BFG Repo-Cleaner , un'alternativa più semplice e veloce a git-filter-branch specificatamente progettata per rimuovere file dalla cronologia di Git. Un modo in cui ti semplifica la vita qui è che in realtà gestisce tutti i riferimenti di default (tutti i tag, rami, ecc.) Ma è anche 10 - 50x più veloce.

Dovresti seguire attentamente i passaggi qui: http://rtyley.github.com / bfg-repo-cleaner / # use - ma il bit principale è proprio questo: scarica BFG jar (richiede Java 6 o versione successiva) ed esegui questo comando:

$ java -jar bfg.jar --delete-files filename.orig my-repo.git

Verrà eseguita la scansione dell'intera cronologia del repository e tutti i file denominati filename.orig (che non si trovano nella latest commit ) verrà rimosso. Ciò è notevolmente più semplice rispetto all'uso di <=> per fare la stessa cosa!

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

You should probably clone your repository first.

Remove your file from all branches history:
git filter-branch --tree-filter 'rm -f filename.orig' -- --all

Remove your file just from the current branch:
git filter-branch --tree-filter 'rm -f filename.orig' -- --HEAD    

Lastly you should run to remove empty commits:
git filter-branch -f --prune-empty -- --all

Solo per aggiungere ciò alla soluzione di Charles Bailey, ho appena usato un git rebase -i per rimuovere i file indesiderati da un commit precedente e ha funzionato come un incantesimo. I passaggi:

# Pick your commit with 'e'
$ git rebase -i

# Perform as many removes as necessary
$ git rm project/code/file.txt

# amend the commit
$ git commit --amend

# continue with rebase
$ git rebase --continue

Il modo più semplice che ho trovato è stato suggerito da leontalbot (come commento), che è a post pubblicato da Anoopjohn.Penso che valga il suo spazio come risposta:

(L'ho convertito in uno script bash)

#!/bin/bash
if [[ $1 == "" ]]; then
    echo "Usage: $0 FILE_OR_DIR [remote]";
    echo "FILE_OR_DIR: the file or directory you want to remove from history"
    echo "if 'remote' argument is set, it will also push to remote repository."
    exit;
fi
FOLDERNAME_OR_FILENAME=$1;

#The important part starts here: ------------------------

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch $FOLDERNAME_OR_FILENAME" -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

if [[ $2 == "remote" ]]; then
    git push --all --force
fi
echo "Done."

Tutti i crediti vanno a Annopjohn, e a leontalbot per averlo sottolineato.

NOTA

Tieni presente che lo script non include convalide, quindi assicurati di non commettere errori e di avere un backup nel caso qualcosa vada storto.Ha funzionato per me, ma potrebbe non funzionare nella tua situazione.USALO CON ATTENZIONE (segui il link se vuoi sapere cosa sta succedendo).

Sicuramente, git filter-branch è la strada da percorrere.

Purtroppo, questo non sarà sufficiente per rimuovere completamente filename.orig dal tuo repository, poiché può ancora essere referenziato da tag, voci di reflog, telecomandi e così via.

Consiglio di rimuovere anche tutti questi riferimenti e di chiamare il Garbage Collector. Puoi usare lo script git forget-blob da questo sito web per fare tutto questo in un solo passaggio.

git forget-blob filename.orig

Se è l'ultimo commit che vuoi ripulire, ho provato con la versione 2.14.3 di Git (Apple Git-98):

touch empty
git init
git add empty
git commit -m init

# 92K   .git
du -hs .git

dd if=/dev/random of=./random bs=1m count=5
git add random
git commit -m mistake

# 5.1M  .git
du -hs .git

git reset --hard HEAD^
git reflog expire --expire=now --all
git gc --prune=now

# 92K   .git
du -hs .git

Questo è ciò per cui git filter-branch è stato progettato per.

Puoi anche usare:

git reset HEAD file/path

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top