In generale, git reset
la funzione è quella di prendere il ramo corrente e resettarlo per puntare altrove, e possibilmente portare l'indice e l'albero di lavoro lungo. Più concretamente, se il tuo ramo principale (attualmente estratto) è così:
- A - B - C (HEAD, master)
e ti rendi conto che vuoi che il master indichi B, non C, lo userai git reset B
per spostarlo lì:
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
Digressione: è diversa da una cassa. Se dovessi correre git checkout B
, otterresti questo:
- A - B (HEAD) - C (master)
Sei finito in uno stato HEAD distaccato. HEAD
, albero di lavoro, indicizza tutte le corrispondenze B
, ma il ramo principale è stato lasciato alle C
. Se effettui un nuovo commit D
a questo punto, otterrai questo, che probabilmente non è quello che desideri:
- A - B - C (master)
\
D (HEAD)
Ricorda, il reset non effettua commit, ma aggiorna solo un ramo (che è un puntatore a un commit) per puntare a un commit diverso. Il resto sono solo dettagli di ciò che accade al tuo indice e all'albero di lavoro.
Casi d'uso
Coprirò molti dei principali casi d'uso git reset
all'interno delle mie descrizioni delle varie opzioni nella sezione successiva. Può davvero essere usato per una grande varietà di cose; il thread comune è che tutti implicano il ripristino del ramo, dell'indice e / o dell'albero di lavoro per puntare / abbinare un determinato commit.
Cose da fare attenzione
--hard
può farti perdere davvero il lavoro. Modifica il tuo albero di lavoro.
git reset [options] commit
può farti perdere (sorta di) commit. Nell'esempio di giocattolo sopra, abbiamo perso il commit C
. È ancora nel repository e puoi trovarlo guardando git reflog show HEAD
o git reflog show master
, ma in realtà non è più accessibile da nessun ramo.
Git elimina definitivamente tali commit dopo 30 giorni, ma fino ad allora puoi recuperare C puntando di nuovo un ramo ( git checkout C; git branch <new branch name>
).
argomenti
Parafrasando la pagina man, l'utilizzo più comune è del modulo git reset [<commit>] [paths...]
, che reimposterà i percorsi dati al loro stato dal commit dato. Se i percorsi non vengono forniti, viene reimpostato l'intero albero e, se il commit non viene fornito, viene considerato HEAD (il commit corrente). Questo è un modello comune tra i comandi git (ad esempio checkout, diff, log, sebbene la semantica esatta vari), quindi non dovrebbe essere troppo sorprendente.
Ad esempio, git reset other-branch path/to/foo
reimposta tutto nel percorso / to / foo al suo stato in altro ramo, git reset -- .
reimposta la directory corrente al suo stato in HEAD e un semplice git reset
reimposta tutto al suo stato in HEAD.
L'albero di lavoro principale e le opzioni di indice
Esistono quattro opzioni principali per controllare cosa succede all'albero di lavoro e all'indice durante il ripristino.
Ricorda, l'indice è "area di gestione temporanea" di git - è dove vanno le cose quando dici git add
in preparazione di impegnarti.
--hard
rende tutto corrispondente al commit a cui hai reimpostato. Questo è probabilmente il più facile da capire. Tutte le modifiche locali vengono bloccate. Un uso primario è spazzare via il tuo lavoro ma non commutare i commit: git reset --hard
significa git reset --hard HEAD
, cioè non cambiare il ramo ma sbarazzarsi di tutti i cambiamenti locali. L'altro è semplicemente spostare un ramo da un posto all'altro e mantenere sincronizzati l'indice / l'albero di lavoro. Questo è quello che può davvero farti perdere il lavoro, perché modifica il tuo albero di lavoro. Assicurati di voler eliminare il lavoro locale prima di eseguirne uno reset --hard
.
--mixed
è il valore predefinito, ovvero git reset
significa git reset --mixed
. Reimposta l'indice, ma non l'albero di lavoro. Questo significa che tutti i tuoi file sono intatti, ma qualsiasi differenza tra il commit originale e quello a cui resetti apparirà come modifiche locali (o file non tracciati) con stato git. Usalo quando ti rendi conto di aver commesso degli errori, ma vuoi mantenere tutto il lavoro che hai fatto in modo da poterlo riparare e ricominciare. Per eseguire il commit, dovrai aggiungere nuovamente i file all'indice ( git add ...
).
--soft
non tocca l'indice o l' albero di lavoro. Tutti i tuoi file sono intatti come con --mixed
, ma tutte le modifiche vengono visualizzate come changes to be committed
con lo stato git (ovvero il check-in in preparazione per il commit). Usalo quando ti rendi conto di aver commesso degli errori, ma tutto va bene - tutto ciò che devi fare è ricominciarlo in modo diverso. L'indice non è stato toccato, quindi puoi eseguire immediatamente il commit, se lo desideri: il commit risultante avrà lo stesso contenuto di quello che eri prima del ripristino.
--merge
è stato aggiunto di recente e ha lo scopo di aiutarti a interrompere una fusione fallita. Ciò è necessario perché git merge
in realtà ti consentirà di eseguire l'unione con un albero di lavoro sporco (uno con modifiche locali) purché tali modifiche si trovino in file non interessati dall'unione. git reset --merge
ripristina l'indice (come --mixed
- tutte le modifiche vengono visualizzate come modifiche locali) e ripristina i file interessati dall'unione, ma lascia gli altri soli. Si spera che questo ripristini tutto come prima della brutta fusione. Di solito lo userai come git reset --merge
(significato git reset --merge HEAD
) perché vuoi solo resettare l'unione, non effettivamente spostare il ramo. ( HEAD
non è stato ancora aggiornato, poiché l'unione non è riuscita)
Per essere più concreti, supponiamo di aver modificato i file A e B e di tentare di unire in un ramo che ha modificato i file C e D. L'unione non riesce per qualche motivo e si decide di interromperlo. Tu usi git reset --merge
. Riporta C e D al modo in cui erano HEAD
, ma lascia le tue modifiche ad A e B da sole, poiché non facevano parte del tentativo di fusione.
Voglio sapere di più?
Penso che man git reset
sia davvero abbastanza buono per questo - forse hai bisogno di un po 'di senso del modo in cui git funziona perché possano davvero affondare. In particolare, se si prende il tempo di leggerli attentamente, quelle tabelle che descrivono in dettaglio gli stati dei file nell'indice e l'albero di lavoro per tutte le varie opzioni e casi sono molto utili. (Ma sì, sono molto densi - stanno trasmettendo moltissime informazioni di cui sopra in una forma molto concisa.)
Notazione strana
La "strana notazione" ( HEAD^
e HEAD~1
) che menzioni è semplicemente una scorciatoia per specificare i commit, senza dover usare un nome di hash come 3ebe3f6
. È completamente documentato nella sezione "specifica delle revisioni" della pagina man per git-rev-parse, con molti esempi e sintassi correlata. Il cursore e la tilde in realtà significano cose diverse :
HEAD~
è l'abbreviazione di HEAD~1
e indica il primo genitore del commit. HEAD~2
indica il primo genitore del primo genitore del commit. Pensa HEAD~n
a "n si impegna prima di HEAD" o "l'antenata generazione di HEAD".
HEAD^
(o HEAD^1
) indica anche il primo genitore del commit. HEAD^2
indica il secondo genitore del commit . Ricorda, un normale commit di unione ha due genitori: il primo genitore è il commit unito e il secondo genitore è il commit che è stato unito. In generale, le fusioni possono effettivamente avere arbitrariamente molti genitori (fusioni di polpo).
- I
^
e ~
operatori possono essere messe insieme, come nella HEAD~3^2
, il secondo genitore del antenato di terza generazione HEAD
, HEAD^^2
il secondo genitore del primo genitore HEAD
, o anche HEAD^^^
, che è equivalente a HEAD~3
.