Git: Come rimuovere il file dall'indice senza eliminare i file da qualsiasi repository


163

Quando si usa

git rm --cached myfile

non cancella dal filesystem locale, che è l'obiettivo. Ma se hai già eseguito il versioning e il commit del file, lo hai trasferito in un repository centrale e l'hai estratto in un altro repository prima di utilizzare il comando, il file verrà eliminato da quel sistema.

C'è un modo per rimuovere semplicemente il file dal controllo delle versioni senza eliminarlo da qualsiasi file system?

Modifica: chiarito, spero.


Forse potresti espandere il tuo caso d'uso. L'indice è l'area di gestione temporanea per il prossimo commit, quindi perché vuoi rimuovere il file dall'indice se non vuoi rimuoverlo dal ramo corrente?
CB Bailey

1
Mi dispiace. Volevo dire che myfile è stato per qualche motivo impegnato e distribuito molto tempo fa. Ora l'obiettivo è rimuoverlo dal controllo delle versioni senza eliminarlo dai sistemi delle persone. Il motivo per cui è venuto fuori è perché ho accidentalmente verificato la versione di un file di configurazione. Il file di configurazione è necessario, quindi non voglio rimuoverlo da sistemi esterni.
Fletcher Moore,

Ho modificato la domanda per essere più chiari.
Fletcher Moore,

3
git rm --cached non rimuoverà il file dall'altra directory di lavoro. Il file verrà rimosso solo se qualcuno che lavora in quella directory esegue un pull. Il file può essere facilmente recuperato con "git checkout HEAD @ {1} pippo" (se eseguito immediatamente dopo il pull.)
William Pursell

Risposte:


119

Non credo che un commit Git possa registrare un'intenzione come "smettere di tracciare questo file, ma non cancellarlo".

L'attivazione di tale intenzione richiederà l'intervento al di fuori di Git in tutti i repository che uniscono (o si rifanno su) un commit che elimina il file.


Salva una copia, Applica eliminazione, Ripristina

Probabilmente la cosa più semplice da fare è dire agli utenti a valle di salvare una copia del file, estrarre la cancellazione, quindi ripristinare il file. Se stanno eseguendo il pull tramite rebase e stanno "apportando" modifiche al file, avranno conflitti. Per risolvere tali conflitti, utilizzaregit rm foo.conf && git rebase --continue (se il commit in conflitto ha delle modifiche oltre a quelle del file rimosso) o git rebase --skip(se il commit in conflitto è stato modificato solo nel file rimosso).

Ripristina il file come non tracciato dopo aver tirato un commit che lo elimina

Se hanno già eseguito il commit della cancellazione, possono comunque recuperare la versione precedente del file con git show :

git show @{1}:foo.conf >foo.conf

O con git checkout (per commento di William Pursell; ma ricordati di rimuoverlo dall'indice!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Se hanno intrapreso altre azioni da quando hanno tirato la tua cancellazione (o stanno tirando con rebase in una TESTA staccata), potrebbero aver bisogno di qualcosa di diverso @{1}. Potrebbero usaregit log -g per trovare il commit appena prima di aver rimosso la tua cancellazione.


In un commento, si menziona che il file che si desidera "non tracciare, ma mantenere" è una sorta di file di configurazione necessario per eseguire il software (direttamente da un repository).

Mantieni il file come "predefinito" e attivalo manualmente / automaticamente

Se non è del tutto inaccettabile continuare a mantenere il contenuto del file di configurazione nel repository, potresti essere in grado di rinominare il file monitorato da (ad esempio) foo.confa foo.conf.defaulte quindi istruire i tuoi utenti a cp foo.conf.default foo.confdopo aver applicato il commit di rinomina. Oppure, se gli utenti utilizzano già una parte esistente del repository (ad esempio uno script o qualche altro programma configurato dal contenuto nel repository (ad esempio Makefileo simile)) per avviare / distribuire il software, è possibile incorporare un meccanismo predefinito nel lancio / processo di distribuzione:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

Con un tale meccanismo inadempiente luogo, gli utenti devono essere in grado di tirare un commit che rinomina foo.confafoo.conf.default senza dover fare alcun lavoro supplementare. Inoltre, si evita di dover copiare manualmente un file di configurazione se si effettuano installazioni / repository aggiuntivi in ​​futuro.

La riscrittura della cronologia richiede comunque un intervento manuale ...

Se è inaccettabile mantenere il contenuto nel repository, probabilmente vorrai sradicarlo completamente dalla storia con qualcosa del genere git filter-branch --index-filter …. Ciò equivale a riscrivere la cronologia, che richiederà un intervento manuale per ciascun ramo / repository (consultare la sezione "Ripristino dal rebase upstream" nella manpage git rebase ). Il trattamento speciale richiesto per il tuo file di configurazione sarebbe solo un altro passo che devi eseguire durante il ripristino dalla riscrittura:

  1. Salva una copia del file di configurazione.
  2. Recupera dalla riscrittura.
  3. Ripristina il file di configurazione.

Ignoralo per prevenire la ricorrenza

Qualunque metodo tu usi, probabilmente vorrai includere il nome del file di configurazione in un .gitignorefile nel repository in modo che nessuno possa inavvertitamente di git add foo.confnuovo (è possibile, ma richiede -f/ --force). Se hai più di un file di configurazione, potresti considerare di "spostarli" tutti in una singola directory e ignorare il tutto ("spostando" intendo cambiare dove si aspetta che il programma trovi i suoi file di configurazione e ottenere gli utenti (o il lancio / meccanismo di deploy) per copiare / spostare i file nella nuova posizione, ovviamente non vorrebbe git mv un file in una directory che vi sarà ignorando).


5
Risposta incredibilmente completa. Grazie!
Fletcher Moore,

La risposta di Tom Power, in basso, sembra contraddire la tua prima frase.
Mike S,

1
@MikeS: l'effetto di --{,no-}assume-unchangedè puramente locale: il suo stato non è registrato direttamente in commit. Può aiutare a impedire che un repository esegua il commit di nuove modifiche al file, ma non lo rimuove dal controllo di versione. Se puoi impostarlo per tutti i tuoi repository pertinenti, correlati e non nudi, potrebbe aiutare la tua situazione, ma non è qualcosa che potresti spingere direttamente + pull / rebase in altri repository correlati, non nudi (specialmente quelli che non controlli, secondo i commenti chiarificatori del richiedente originale sulla domanda: vedi "sistemi delle persone" / "sistemi stranieri").
Chris Johnsen,

1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- ora può, congit rm --cached foo.conf
Nick Volynkin il

1
@NickVolynkin: la domanda non indica già che questo è inadeguato allo scopo del richiedente: (automaticamente) mantenere il file in giro quando il commit risultante viene inserito in un altro repository?
Chris Johnsen,

86

Ho avuto lo stesso problema questa settimana quando ho commesso per errore, quindi ho provato a rimuovere un file di build da un repository condiviso e questo:

http://gitready.com/intermediate/2009/02/18/temporarily-ignoring-files.html

ha funzionato bene per me e non è stato menzionato finora.

git update-index --assume-unchanged <file>

Per rimuovere il file che ti interessa dal controllo versione, quindi utilizza tutti gli altri comandi normalmente.

git update-index --no-assume-unchanged <file>

Se hai mai voluto rimetterlo dentro.

Modifica: si prega di vedere i commenti di Chris Johnsen e KPM, questo funziona solo localmente e il file rimane sotto il controllo della versione per altri utenti se non lo fanno anche. La risposta accettata fornisce metodi più completi / corretti per gestirlo. Inoltre alcune note dal link se si utilizza questo metodo:

Ovviamente ci sono alcuni avvertimenti che entrano in gioco con questo. Se si aggiunge direttamente il file, verrà aggiunto all'indice. L'unione di un commit con questo flag attivo provocherà un corretto fallimento dell'unione in modo da poterlo gestire manualmente.


7
Questa non è una risposta per il problema specifico menzionato. Funzionerà solo per il tuo repository locale, quindi ogni utente deve farlo da solo. È un dolore
KPM,

28

Per rimuovere il file dall'indice, utilizzare:

git reset myfile

Ciò non dovrebbe influire sulla tua copia locale o di chiunque altro.


1
resetrimuove il file dall'indice solo se il file non si trova nel commit HEAD corrente, altrimenti ripristina solo la versione dell'indice alla versione HEAD corrente.
CB Bailey

1
Forse ho frainteso la domanda, ma sembra riguardare la rimozione di un file dall'indice senza influenzare alcun commit.
Armand,

Come ha detto Charles, il reset non "rimuove un file". Digita git help reset per maggiori informazioni.
Simon B.

4
Questo risponde alla domanda intitolata "Come rimuovere il file dall'indice senza eliminare i file da qualsiasi repository". Sebbene l'OP chiedesse davvero "Come posso annullare la traccia di un file senza eliminare la copia locale".
Hewsonism,

@hewsonism l'OP ha detto "qualsiasi filesystem" (anche prima di qualsiasi modifica).
jbobbins,


15

Dopo aver eseguito il git rm --cachedcomando, prova ad aggiungerlo myfileal .gitignorefile (creane uno se non esiste). Questo dovrebbe dire a Git di ignorare myfile.

Il .gitignorefile ha una versione, quindi è necessario eseguirne il commit e inviarlo al repository remoto.


1

La mia soluzione è quella di estrarre l'altra copia di lavoro e quindi fare:

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

che dice ottenere tutti i percorsi dei file nell'ultimo commento e controllarli dal padre di HEAD. Lavoro fatto.


0

Le soluzioni di cui sopra funzionano bene nella maggior parte dei casi. Tuttavia, se è necessario rimuovere anche tutte le tracce di quel file (ovvero dati sensibili come le password), è necessario rimuoverlo dall'intera cronologia di commit, poiché il file potrebbe comunque essere recuperato da lì.

Ecco una soluzione che rimuove tutte le tracce del file dalla tua intera cronologia di commit, come se non fosse mai esistito, mantenendo comunque il file sul tuo sistema.

https://help.github.com/articles/remove-sensitive-data/

Puoi effettivamente andare al passaggio 3 se ti trovi nel tuo repository git locale e non devi eseguire una corsa a secco. Nel mio caso, avevo solo bisogno dei passaggi 3 e 6, dato che avevo già creato il mio file .gitignore ed era nel repository su cui volevo lavorare.

Per visualizzare le modifiche, potrebbe essere necessario andare alla radice GitHub del repository e aggiornare la pagina. Quindi navigare attraverso i collegamenti per ottenere un vecchio commit che una volta aveva il file, per vedere che ora è stato rimosso. Per me, semplicemente aggiornare la vecchia pagina di commit non ha mostrato la modifica.

All'inizio sembrava intimidatorio, ma in realtà era facile e funzionava come un incantesimo! :-)

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.