Perché ci sono due modi per mettere in scena un file in Git?


1169

A volte git suggerisce git rm --cacheddi mettere in scena un file, a volte git reset HEAD file. Quando dovrei usare quale?

MODIFICARE:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

20
Perché? Direi che l'interfaccia di riga di comando di git si è evoluta in modo organico e non è mai stata soggetta a importanti ristrutturazioni per rendere le cose coerenti. (Se non siete d'accordo, nota come git rmpuò sia palco una delezione e anche unstage un'addizione )
Romano Starkov

3
@romkyns: sono d'accordo sul fatto che l'interfaccia di Git ha diverse stranezze perché si è evoluta organicamente, ma una rimozione è sicuramente una funzione inversa di un'aggiunta, quindi non è logico rmannullarla add? Come pensi che rmdovrebbe comportarsi?
Zaz,

6
L'unica risposta effettiva alla tua domanda è che subito dopo git initnon c'è nessun HEADa cui ripristinare.
Miles Rout


4
@Zaz, darò la mia opinione. rmimplica la cancellazione in un contesto unix. Non è l'opposto dell'aggiunta all'indice. Una funzione per rimuovere i file non deve essere sovraccaricata di funzioni per modificare lo stato di gestione temporanea. Se ci sono dettagli di implementazione che rendono questi comodi da combinare, ciò indica semplicemente la mancanza di uno strato premuroso di astrazione in git, il che renderebbe chiara l'usabilità.
Joshua Goldberg,

Risposte:


1893

git rm --cached <filePath> non mette in scena un file, in realtà mette in scena la rimozione dei file dal repository (supponendo che sia già stato eseguito il commit in precedenza) ma lascia il file nell'albero di lavoro (lasciandoti con un file non tracciato).

git reset -- <filePath>sarà unstage qualsiasi messo in scena le modifiche per il file di data (s).

Detto questo, se hai utilizzato git rm --cachedun nuovo file messo in scena, sembrerebbe praticamente che tu non lo abbia mai messo in scena poiché non lo aveva mai eseguito prima.

Aggiorna git 2.24
In questa nuova versione di git puoi usare git restore --stagedinvece di git reset. Vedi documenti git .


71
Direi di git rm --cachedannullare la messa in scena del file ma non lo rimuove dalla directory di lavoro.
Pierre de LESPINAY,

4
Per rimuovere un file messo in scena per l'aggiunta in modo che non sia più messo in scena, si può sicuramente chiamare "unstaging di un file messo in scena per aggiunta", giusto? Il risultato finale non è una cancellazione graduale , questo è certo, quindi penso che l'incomprensione sia totalmente comprensibile.
Roman Starkov,

4
Quindi, in genere, si userebbe git rm --cached <filePath>per rimuovere alcuni file dal repository dopo aver realizzato che non avrebbe mai dovuto essere nel repository: quindi molto probabilmente eseguendo questo comando e quindi aggiungendo i file rilevanti a gitignore. Ho ragione?
Adrien Be

13
Con così tanti voti sia sulla domanda che sulla risposta, direi che apparentemente vogliamo avere un unstagecomando git.
milosmns,

4
"git status" avvisa ora: usa "git restore --staged <file> ..." per rimuovere la scena
yucer

334

git rm --cachedviene utilizzato per rimuovere un file dall'indice. Nel caso in cui il file sia già nel repository, git rm --cachedverrà rimosso il file dall'indice, lasciandolo nella directory di lavoro e un commit lo rimuoverà anche dal repository. Fondamentalmente, dopo il commit, avresti annullato la modifica del file e conservato una copia locale.

git reset HEAD file(che per impostazione predefinita utilizza il --mixedflag) è diverso da quello nel caso in cui il file sia già nel repository, sostituisce la versione dell'indice del file con quella del repository (HEAD), annullando effettivamente la modifica delle modifiche ad esso.

Nel caso di un file senza versione, verrà messo in scena l'intero file poiché il file non era presente in HEAD. In questo aspetto git reset HEAD filee git rm --cachedsono uguali, ma non sono gli stessi (come spiegato nel caso di file già presenti nel repository)

Alla domanda di Why are there 2 ways to unstage a file in git?- non c'è mai davvero solo un modo per fare qualcosa di strano. questa è la bellezza :)


7
Sia la risposta accettata che questa sono ottime e spiegano perché dovresti usare l'una contro l'altra. Ma non rispondono direttamente alla domanda implicita del perché Git suggerisce due diversi metodi. Nel primo caso nell'esempio del PO, è stato appena fatto un init git. In quel caso, git suggerisce "git rm --cached" perché a quel punto non ci sono commit nel repository e quindi HEAD non è valido. "git reset HEAD - a" produce: "fatale: impossibile risolvere" HEAD "come riferimento valido."
sootsnoot,

5
con 'git checkout', non perderesti tutte le modifiche che hai apportato al file? Non è la stessa cosa di annullare la messa in scena di un file, a meno che non mi fraintenda.
John Deighan,

there is never really only one way to do anything in git. that is the beauty of it- Hmm ... perché? è sempre fantastico, quando c'è solo un modo ovvio. questo ci consente di risparmiare molto tempo e memoria nel nostro cervello))
Oto Shavadze,

128

Abbastanza semplice:

  • git rm --cached <file> fa in modo che git interrompa completamente il tracciamento del file (lasciandolo nel filesystem, a differenza di plain git rm*)
  • git reset HEAD <file> rimuove tutte le modifiche apportate al file dall'ultimo commit (ma non le ripristina nel filesystem, contrariamente a quanto potrebbe suggerire il nome del comando **). Il file rimane sotto controllo di revisione.

Se prima il file non era in controllo di revisione (ovvero non stai mettendo in scena un file che hai appena editato git addper la prima volta), i due comandi hanno lo stesso effetto, quindi l'aspetto di questi sono "due modi di fare qualcosa ".

* Tieni presente le avvertenze menzionate da @DrewT nella sua risposta, per quanto riguarda git rm --cachedun file che era stato precedentemente impegnato nel repository. Nel contesto di questa domanda, di un file appena aggiunto e non ancora impegnato, non c'è nulla di cui preoccuparsi.

** Ho avuto paura per un tempo imbarazzante per usare il comando git reset a causa del suo nome - e ancora oggi cerco spesso la sintassi per assicurarmi di non sbagliare. ( aggiornamento : ho finalmente preso il tempo di riassumere l'uso di git resetin una pagina tldr , quindi ora ho un modello mentale migliore di come funziona e un rapido riferimento per quando dimentico alcuni dettagli.)


Ègit rm <file> --cached
neonmate il

8
Non credo davvero che la modifica del 4 agosto 2015 a questa risposta sia stata un miglioramento complessivo. Potrebbe aver corretto la correttezza tecnica (non mi sento qualificato per valutarlo), ma temo che abbia reso il tono della risposta molto meno accessibile, introducendo un linguaggio come "disattiva l'imperativo per iniziare a tracciare un file attualmente non tracciato ", e usando il gergo come" index "e" HEAD ", precisamente il tipo di cose che spaventa i principianti. Se qualcuno può, modifica per ripristinare una lingua più adatta ai nuovi arrivati.
waldyrious

5
Sono d'accordo con @valyrious. La risposta originale potrebbe non essere stata direttamente dal manuale di git, ma ha risposto alla domanda a un livello tecnico sufficiente. I dettagli tecnici avrebbero dovuto essere chiariti nei commenti, non come una modifica che oscurasse l'intento originale.
Simon Robb,

Ho ripristinato la modifica. Credo che ci sia stata una convalida sufficiente da parte della comunità (nei commenti precedenti e nei voti su di essi) che la modifica fosse dannosa per la chiarezza della risposta.
waldyrious il

Nota @DrewT avverte che se si utilizza rm --cachede si spinge, chiunque tiri lo stesso ramo avrà i file effettivamente rimossi dal proprio albero di lavoro.
Tom Hale,

53

Questo thread è un po 'vecchio, ma voglio ancora aggiungere una piccola dimostrazione poiché non è ancora un problema intuitivo:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(senza -q) fornisce un avviso sul file modificato e il suo codice di uscita è 1, che verrà considerato un errore in uno script.

Modifica: git checkout HEAD to-be-modified to-be-removedfunziona anche per la non messa in scena, ma rimuove completamente la modifica dallo spazio di lavoro

Aggiornamento git 2.23.0: di volta in volta, i comandi cambiano. Ora git statusdice:

  (use "git restore --staged <file>..." to unstage)

... che funziona per tutti e tre i tipi di modifiche


Grazie, non è stato del tutto chiaro dalle prime due risposte (probabilmente solo la mia ignoranza sulla terminologia) che git reset ha lasciato localmente le modifiche nel file (al contrario di git checkout che le avrebbe ripristinate).
soupdog,

Dovresti mettere un avvertimento all'inizio della versione, perché la vecchia versione elimina i file nelle nuove versioni
Luis Mauricio

@LuisMauricio quale comando cancella i file?
Daniel Alder,

@DanielAlder sry, ho appena testato di nuovo, non elimina, errore mio.
Luis Mauricio

36

se hai messo in scena file per errore che non desideri impegnare e desideri essere certo di mantenere le modifiche, puoi anche utilizzare:

git stash
git stash pop

questo esegue un reset su HEAD e riapplica le modifiche, consentendo di ri-mettere in scena singoli file per il commit. questo è utile anche se hai dimenticato di creare un ramo di funzionalità per le richieste pull ( git stash ; git checkout -b <feature> ; git stash pop).


3
Questa è una soluzione pulita e molto meno preoccupante della digitazione di "git rm"
Subimage

1
git stashha altri vantaggi correlati, perché crea voci nel reflog che saranno poi disponibili in futuro. in caso di dubbio, vai avanti e fai un git stash(es. git stash save -u "WIP notes to self"('-u' deve includere tutti i file nuovi / non tracciati nel commit stash) ... quindi prova git reflog show stasha vedere l'elenco dei commit stash e dei loro sha. Consiglio una shell alias likealias grs="git reflog show stash"
bisettimanale

15

Questi 2 comandi presentano diverse sottili differenze se il file in questione si trova già nel repository e sotto il controllo della versione (precedentemente eseguito il commit ecc.):

  • git reset HEAD <file> sblocca il file nel commit corrente.
  • git rm --cached <file>metterà in scena il file anche per futuri commit. Non viene messo in scena fino a quando non viene nuovamente aggiunto git add <file>.

E c'è un'altra differenza importante:

  • Dopo aver eseguito git rm --cached <file>e spinto il tuo ramo sul telecomando, chiunque estrae il tuo ramo dal telecomando otterrà il file REALMENTE cancellato dalla loro cartella, anche se nel tuo set di lavoro locale il file non viene più tracciato (cioè non cancellato fisicamente dalla cartella).

Quest'ultima differenza è importante per i progetti che includono un file di configurazione in cui ogni sviluppatore del team ha una configurazione diversa (ad es. Url di base, impostazioni IP o porte diverse), quindi se stai usando git rm --cached <file>qualcuno che tira il tuo ramo dovrai ri crea la configurazione, oppure puoi inviarla tua e loro possono modificarla nuovamente nelle loro impostazioni ip (ecc.), perché l'eliminazione ha effetto solo sulle persone che ritirano il tuo ramo dal telecomando.


10

Diciamo che è stageun'intera directory tramite git add <folder>, ma si desidera escludere un file dall'elenco graduale (ovvero l'elenco che genera durante l'esecuzione git status) e mantenere le modifiche all'interno del file escluso (si stava lavorando su qualcosa e non è pronto per il commit, ma non vuoi perdere il lavoro ...). Puoi semplicemente usare:

git reset <file>

Quando si esegue git status, si vedrà che tutto ciò che il file (s) che si resetè unstagede il resto dei file che addedsono ancora nella stagedlista.


10

1.

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(usa "git rm --cached ..." per annullare la fase)

  • git è un sistema di puntatori

  • non hai ancora un impegno per modificare il puntatore

  • l'unico modo per "estrarre i file dal bucket indicato" è rimuovere i file che hai detto a git di controllare le modifiche

2.

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

git commit -ma

  • hai commesso " salvato "

3.

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(usa "git reset HEAD ..." per annullare la messa in scena)

  • hai effettuato un commit nel tuo codice in questo momento
  • ora puoi reimpostare il puntatore sul commit " ripristina all'ultimo salvataggio "

1
Questa è in realtà l'unica risposta che risponde correttamente alla domanda, IMO. In realtà risponde alla domanda, che non è "quali sono le differenze tra" git rm --cached "e" git reset HEAD "ma" perché git fornisce in modo incoerente entrambe le opzioni? ", La risposta è che non c'è HEAD da ripristinare a quando tu git initper la prima volta.
Miles Rout

5

Sono sorpreso che nessuno abbia menzionato il reflog di git ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

Il reflog è una cronologia git che non solo tiene traccia delle modifiche al repository, ma tiene anche traccia delle azioni dell'utente (ad es. Pull, checkout in un ramo diverso, ecc.) E consente di annullare tali azioni. Quindi, invece di annullare la messa in scena del file che è stato messo in scena per errore, dove è possibile tornare al punto in cui non sono stati messi in scena i file.

Questo è simile git reset HEAD <file>ma in alcuni casi può essere più granulare.

Mi dispiace - non rispondo davvero alla tua domanda, ma sto solo indicando un altro modo per estrarre i file che uso abbastanza spesso (io per esempio mi piacciono le risposte di Ryan Stewart e waldyrious.);) Spero che sia di aiuto.


5

Usa solo:

git reset HEAD <filename>

In questo modo il file viene messo in scena e vengono mantenute le modifiche apportate, quindi, a sua volta, è possibile modificare i rami se lo si desidera e git addtali file in un altro ramo. Tutte le modifiche vengono mantenute.


3

Mi sembra che git rm --cached <file>rimuova il file dall'indice senza rimuoverlo dalla directory in cui una pianura git rm <file>farebbe entrambe le cose, proprio come un sistema operativo rm <file>rimuove il file dalla directory senza rimuoverne il controllo delle versioni.


1

Solo per le versioni 2.23 e successive,

Invece di questi suggerimenti, è possibile utilizzare git restore --staged <file>per unstagei file.


Funziona con entrambe le opzioni --stagee --staged.
dhana1310
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.