Qual è la differenza tra git reset --mixed, --soft e --hard?


741

Sto cercando di dividere un commit su e non sono sicuro quale opzione di ripristino da utilizzare.

Stavo guardando la pagina In parole povere, cosa fa "git reset"? , ma mi sono reso conto di non capire davvero quale sia l'indice git o l'area di gestione temporanea e quindi le spiegazioni non sono state di aiuto.

Inoltre, i casi d'uso --mixede mi --softsembrano uguali in quella risposta (quando si desidera risolvere e ricominciare). Qualcuno può scomporlo ancora di più? Mi rendo conto che --mixedè probabilmente l'opzione da seguire, ma voglio sapere perché . Infine, che dire --hard?

Qualcuno può darmi un esempio di flusso di lavoro su come potrebbe avvenire la selezione delle 3 opzioni?


1
Vado a modificare la mia risposta su quell'altra domanda per provare a renderlo un po 'più chiaro.
Cascabel,

La risposta di @mkarasek è piuttosto buona, ma si potrebbe essere interessati a dare un'occhiata anche a questa domanda .
brandizzi,

3
Nota per sé: In generale , soft: stage everything, mixed: unstage everything, hard: ignore everythingfino alla commettere sto reset da.
user1164937


un altro buon articolo di David Zychchiara spiegazione - davidzych.com/difference-between-git-reset-soft-mixed-and-hard
src3369

Risposte:


1489

Quando si modifica un file nel proprio repository, la modifica inizialmente non viene messa in scena. Per impegnarlo, è necessario metterlo in scena, cioè aggiungerlo all'indice, usando git add. Quando si effettua un commit, le modifiche che vengono eseguite sono quelle che sono state aggiunte all'indice.

git resetcambia, come minimo, dove HEADsta puntando il ramo corrente ( ). La differenza tra --mixede --softè se anche l'indice viene modificato. Quindi, se siamo in filiale mastercon questa serie di commit:

- A - B - C (master)

HEADpunti Ce l'indice corrisponde C.

Quando eseguiamo git reset --soft B, master(e quindi HEAD) ora punta a B, ma l'indice ha ancora le modifiche da C; git statusli mostrerà come messi in scena. Quindi, se corriamo git commita questo punto, avremo un nuovo commit con le stesse modifiche di C.


Ok, quindi a partire da qui di nuovo:

- A - B - C (master)

Adesso facciamolo git reset --mixed B. (Nota: --mixedè l'opzione predefinita). Ancora una volta, mastere HEADpunta a B, ma questa volta anche l'indice viene modificato per corrispondere B. Se corriamo git commita questo punto, non accadrà nulla poiché l'indice corrisponde HEAD. Abbiamo ancora le modifiche nella directory di lavoro, ma poiché non sono nell'indice, le git statusmostrano come non messe in scena. Per impegnarli, lo faresti git adde poi ti impegnerai come al solito.


E infine, --hardè lo stesso di --mixed(cambia il tuo HEADe l'indice), tranne che --hardmodifica anche la tua directory di lavoro. Se ci troviamo Ced eseguiamo git reset --hard B, le modifiche aggiunte C, così come eventuali modifiche non confermate, verranno rimosse e i file nella copia di lavoro corrisponderanno a commit B. Dal momento che puoi perdere definitivamente le modifiche in questo modo, dovresti sempre eseguire git statusprima di eseguire un hard reset per assicurarti che la tua directory di lavoro sia pulita o che tu stia bene a perdere le modifiche non confermate.


E infine una visualizzazione: inserisci qui la descrizione dell'immagine


45
In altre parole, --soft sta scartando l'ultimo commit, --mix sta scartando l'ultimo commit e aggiunge, --hard sta scartando l'ultimo commit, aggiungi e tutte le modifiche apportate ai codici che è lo stesso con git checkout HEAD
James Wang

11
@eventualEntropy È possibile recuperare eventuali modifiche impegnate con il reflog; le modifiche non confermate che vengono rimosse non reset --hardsono più presenti.
mkarasek,

2
@Robert Né; --mixedcambia il tuo indice ma non la tua directory di lavoro, quindi eventuali modifiche locali non sono interessate.
mkarasek,

3
Può essere utile per le persone visive che usano git sul terminale con il colore: 1. 'reset reset - soft A' e vedrai le cose di B e C in verde (messa in scena) 2. 'reset reset - mixato A' e vedi le cose di B e C in rosso (non messo in scena) 3. 'resetta --hard A' e non vedrai più le modifiche di B e C da nessuna parte (sarà come se non fossero mai esistite)
timhc22

2
@ user1933930 1 e 3 ti lasceranno - A - B - C′, dove C ′ contiene le stesse modifiche di C (con data / ora diversa e possibilmente messaggio di commit). 2 e 4 ti lasceranno - A - D, dove D contiene le modifiche combinate di B e C.
mkarasek,

216

In parole povere:

  • --soft: annulla modifiche, le modifiche vengono lasciate in scena ( indice ).
  • --mixed (impostazione predefinita) : decomprimere + modifiche sul palco , le modifiche vengono lasciate nell'albero di lavoro .
  • --hard: uncommit + unstage + elimina modifiche, non è rimasto nulla.

8
migliore risposta perché la risposta usa termini tecnici per fornire una risposta completa che è anche la più concisa
Trevor Boyd Smith

1
Quando ho eseguito il commit di un file (non compresso) e ho un file non tracciato appena creato, quindi git reset: chi non fa nulla? Solo quando metto in scena il file non tracciato, lo rimuove dalla mia directory di lavoro.
Michael,

1
@Nikhil Puoi spiegare dove questa risposta è sbagliata?
Ned Batchelder,

1
@NedBatchelder Nessuno dei punti è corretto: poiché il commit non avviene mai quando si utilizzano questi comandi.
Nikhil,

1
@Nikhil Forse quello che vuoi dire è che il commit originale esiste ancora, il che è vero. Ma il ramo è stato modificato in modo che il commit non faccia più parte del ramo. Siamo d'accordo su questo?
Ned Batchelder,

69

Si noti che questa è una spiegazione semplificata intesa come primo passo nel cercare di comprendere questa complessa funzionalità.

Può essere utile per gli studenti visivi che desiderano visualizzare l'aspetto del loro progetto dopo ciascuno di questi comandi:


Per coloro che usano Terminal con il colore acceso (git config --global color.ui auto):

git reset --soft A e vedrai la roba di B e C in verde (messa in scena e pronta per il commit)

git reset --mixed A(o git reset A) e vedrai le cose di B e C in rosso (non messo in scena e pronto per essere messo in scena (verde) e poi impegnato)

git reset --hard A e non vedrai più le modifiche di B e C da nessuna parte (sarà come se non fossero mai esistite)


O per coloro che usano un programma GUI come 'Tower' o 'SourceTree'

git reset --soft A e vedrai le cose di B e C nell'area 'file in scena' pronte per il commit

git reset --mixed A(o git reset A) e vedrai le cose di B e C nell'area 'file non in scena' pronte per essere spostate in scena e poi impegnate

git reset --hard A e non vedrai più le modifiche di B e C da nessuna parte (sarà come se non fossero mai esistite)


1
Questo è fuorviante, nella migliore delle ipotesi: la tua risposta si legge come se git resetcambiasse solo l'aspetto git statusdell'output.
jub0bs

3
Vedo il tuo punto, ma non sono d'accordo perché, come studente visivo, vedere come il mio progetto "sembrava" dopo aver usato i 3 comandi mi ha aiutato finalmente a capire cosa stavano facendo!
timhc22,

L'ho visto più come un'idea di "git for dummies" per aiutare le persone a facilitare ciò che sta realmente accadendo. Riesci a pensare a come potrebbe essere migliorato in modo da non essere fuorviante
timhc22

8
No, non abbiamo bisogno di cambiare questa risposta. Fornisce un pratico "cheat sheet". Pensaci: morbido = verde, misto = rosso, difficile = niente (significa andato)! Che facile da ricordare! Per quei neofiti che non capiscono nemmeno cosa significhino davvero quei colori, sanno troppo poco di git, e prenderanno comunque lezioni difficili lungo la strada, e NON è colpa di @unegma! A proposito, ho appena votato questa risposta per contrastare quel precedente downvote. Ottimo lavoro, @unegma!
RayLuo,

5
Questo è stato un ottimo riassunto supplementare per comprendere meglio i meccanismi interni mentre li leggo altrove. Grazie!
spex

24

Tutte le altre risposte sono grandi, ma io lo trovo migliore per capire loro, abbattendo i file in tre categorie: unstaged, staged, commit:

  • --hard dovrebbe essere facile da capire, ripristina tutto
  • --mixed (impostazione predefinita) :
    1. unstagedfile: non cambiare
    2. staged file: passa a unstaged
    3. commit file: passa a unstaged
  • --soft:
    1. unstagedfile: non cambiare
    2. stagedfile: non cambiare
    3. commit file: passa a staged

In sintesi:

  • --softL'opzione sposta tutto (tranne i unstagedfile) instaging area
  • --mixed L'opzione sposta tutto in unstaged area

22

Ecco una spiegazione di base per gli utenti di TortoiseGit:

git reset --softe --mixedlascia intatti i tuoi file.

git reset --hardmodifica effettivamente i tuoi file in modo che corrispondano al commit a cui ripristini.

In TortoiseGit, il concetto di indice è molto nascosto dalla GUI. Quando si modifica un file, non è necessario eseguire git addper aggiungere la modifica all'area / indice di gestione temporanea. Quando si tratta semplicemente di modificare i file esistenti che non cambiano i nomi dei file git reset --softe --mixedsono uguali! Noterai una differenza solo se hai aggiunto nuovi file o rinominato file. In questo caso, se esegui git reset --mixed, dovrai aggiungere nuovamente i tuoi file dall'elenco File non versioni .


Questa risposta non è molto chiara sulla differenza tra soft e mixed. ed è persino sprezzante nel dichiararlo. La seguente risposta è più chiara al riguardo. stackoverflow.com/questions/2530060/...
barlop

2
Come utente di Github Desktop che ha anche lo stesso comportamento, questa risposta mi dà un po 'di chiarezza sul perché rimango confuso su --mixede --soft.
Chen Li Yong

20

In questi casi mi piace un elemento visivo che si spera possa spiegare questo:

git reset --[hard/mixed/soft] :

inserisci qui la descrizione dell'immagine

Quindi ogni effetto ha ambiti diversi

  1. Hard => WorkingDir + Index + HEAD
  2. Misto => Indice + TESTA
  3. Soft => Solo HEAD (indice e directory di lavoro invariati).

15

Tre tipi di rimpianti

Molte delle risposte esistenti non sembrano rispondere alla domanda reale. Riguardano ciò che fanno i comandi, non ciò che tu (l'utente) desideri: il caso d'uso . Ma questo è ciò di cui l'OP ha chiesto!

Potrebbe essere più utile formulare la descrizione in termini di ciò che è proprio il rimpianto al momento in cui si dà un git resetcomando. Diciamo che abbiamo questo:

A - B - C - D <- HEAD

Ecco alcuni possibili rimpianti e cosa fare al riguardo:

1. Mi dispiace che B, C e D non siano un commit.

git reset --soft A. Ora posso eseguire immediatamente il commit e presto, tutte le modifiche poiché A sono un commit.

2. Mi dispiace che B, C e D non siano dieci commit.

git reset --mixed A. I commit sono andati e l'indice è tornato su A, ma l'area di lavoro sembra ancora come dopo D. Quindi ora posso aggiungere e impegnare in un raggruppamento completamente diverso.

3. Mi dispiace che B, C e D siano avvenuti su questo ramo ; Vorrei essermi ramificato dopo A e sono successe su quell'altro ramo.

Crea un nuovo ramo otherbranche quindi git reset --hard A. Il ramo corrente ora termina in A, con otherbranchderivazione da esso.

(Naturalmente puoi anche usare un hard reset perché desideri che B, C e D non siano mai accaduti affatto.)


5

Non devi forzarti a ricordare le differenze tra di loro. Pensa a come hai effettivamente fatto un commit.

1. Apporta alcune modifiche.

2. aggiungi add.

3.gc -m "Ho fatto qualcosa"

Soft, Mixed e Hard è il modo che ti consente di rinunciare alle operazioni che hai eseguito da 3 a 1.

Soft "fingeva" di non vedere mai di aver fatto "gc -m".

Misto "fingendo" di non vedere mai che hai fatto "git add".

Difficile "fingere" di non vedere mai che hai apportato modifiche ai file.


4

Prima di entrare in queste tre opzioni bisogna capire 3 cose.

1) Storia / TESTA

2) Stage / indice

3) Directory di lavoro

reset - soft: Cronologia modificata, HEAD modificata, Directory di lavoro non modificata.

reset --mix: cronologia modificata, HEAD modificata, directory di lavoro modificata con dati non messi in scena.

reset --hard: cronologia modificata, HEAD modificata, directory di lavoro modificata con dati persi.

Git --soft è sempre sicuro. Si dovrebbe usare un'altra opzione in requisiti complessi.


3

Ci sono un certo numero di risposte qui con un'idea sbagliata git reset --soft. Sebbene vi sia una condizione specifica in cui git reset --softcambierà HEAD(a partire da uno stato di testa staccato), in genere (e per l'uso previsto), sposta il riferimento di ramo che hai attualmente estratto. Ovviamente non può farlo se non hai un ramo estratto (quindi la condizione specifica in cui git reset --softcambierà solo HEAD).

Ho trovato che questo è il modo migliore di pensare git reset. Non ti stai solo muovendo HEAD( tutto lo fa ), stai anche spostando il riferimento del ramo , ad es master. Questo è simile a ciò che accade quando si esegue git commit(il ramo corrente si sposta insieme a HEAD), tranne che invece di creare (e passare a) un nuovo commit, si passa a un precedente commit .

Questo è il punto reset, cambiare un ramo in qualcosa di diverso da un nuovo commit, non cambiare HEAD. Puoi vederlo nell'esempio di documentazione:

Annulla un commit, rendendolo un ramo argomento

          $ git branch topic/wip     (1)
          $ git reset --hard HEAD~3  (2)
          $ git checkout topic/wip   (3)
  1. Hai fatto alcuni commit, ma ti rendi conto che erano prematuri nel ramo "master". Volete continuare a lucidarli in un ramo argomento, quindi create un ramo "topic / wip" fuori dall'HEAD corrente.
  2. Riavvolgi il ramo principale per sbarazzarti di questi tre commit.
  3. Passa al ramo "topic / wip" e continua a lavorare.

Qual è il punto di questa serie di comandi? Vuoi spostare un ramo , qui master, quindi mentre hai fatto il mastercheck-out, corrigit reset .

La risposta più votata qui è generalmente buona, ma ho pensato di aggiungere questo per correggere le diverse risposte con idee sbagliate.

Cambia il tuo ramo

git reset --soft <ref>: reimposta il puntatore del ramo per il ramo attualmente estratto sul commit al riferimento specificato, <ref> . I file nella directory di lavoro e nell'indice non vengono modificati. Commettere da questa fase ti riporterà al punto in cui eri prima del git resetcomando.

Cambia anche il tuo indice

git reset --mixed <ref>

o equivalentemente

git reset <ref>:

Fa ciò che --softfa AND reimposta anche l'indice in modo che corrisponda al commit nel riferimento specificato. Mentre git reset --soft HEADnon fa nulla (perché dice spostare il ramo estratto nel ramo estratto) git reset --mixed HEAD, o equivalentementegit reset HEAD , è un comando comune e utile perché reimposta l'indice sullo stato dell'ultimo commit.

Cambia anche la tua directory di lavoro

git reset --hard <ref>: fa quello che --mixedfa E sovrascrive anche la tua directory di lavoro. Questo comando è simile git checkout <ref>, tranne per il fatto che (e questo è il punto cruciale reset) tutte le forme di git resetspostamento HEADa cui punta il ramo .

Una nota su "tale e tale comando sposta la TESTA":

Non è utile dire che un comando sposta il HEAD. Qualsiasi comando che cambi dove ti trovi nella cronologia di commit sposta HEAD. Questo è ciò che HEAD è , un puntatore a ovunque tu sia. HEADsei tu , e così si sposterà ogni volta che lo fai.


2
"spostare il riferimento del ramo": buon punto. Ho dovuto aggiornare stackoverflow.com/a/5203843/6309 .
VonC

1

Una breve risposta in quale contesto vengono utilizzate le 3 opzioni:

Per mantenere le modifiche correnti nel codice ma per riscrivere la cronologia di commit:

  • soft: Puoi eseguire il commit di tutto in una volta e creare un nuovo commit con una nuova descrizione (se usi torotise git o la maggior parte delle altre GUI, questa è quella da usare, poiché puoi ancora spuntare quali file vuoi nel commit e fare più si impegna in questo modo con file diversi. In Sourcetree tutti i file vengono messi in scena per il commit.)
  • mixed: Dovrai aggiungere nuovamente i singoli file all'indice prima di eseguire i commit (in Sourcetree tutti i file modificati non verranno messi in scena)

Per perdere effettivamente anche le modifiche nel codice:

  • hard: non solo riscrivi la cronologia ma perdi anche tutte le modifiche fino al punto ripristinato

In questo caso non mi ammorbidisco. Se devi impegnarti, cosa è stato ripristinato? stai commettendo il ripristino o stai raccomandando le modifiche (quindi tornare allo stato originale?)
John Little,

Raccomandare le modifiche. Non ci sarà alcun commit inverso.
Nickpick,

1

Le differenze di base tra le varie opzioni del comando git reset sono le seguenti.

  • --soft: reimposta HEAD solo sul commit selezionato. Funziona sostanzialmente come il checkout di Git ma non crea uno stato head distaccato.
  • --mix (opzione predefinita): reimposta HEAD sul commit selezionato nella cronologia e annulla le modifiche nell'indice.
  • --hard: reimposta HEAD sul commit selezionato nella cronologia, annulla le modifiche nell'indice e annulla le modifiche nella directory di lavoro.

1

--soft: Dice a Git di ripristinare HEAD su un altro commit, quindi l'indice e la directory di lavoro non verranno modificati in alcun modo. Tutti i file modificati tra HEAD originale e il commit verranno messi in scena.

--mixed: Proprio come il soft, questo ripristinerà HEAD su un altro commit. Ripristinerà anche l'indice in modo che corrisponda mentre la directory di lavoro non verrà toccata. Tutte le modifiche rimarranno nella directory di lavoro e appariranno come modificate, ma non messe in scena.

--hard: Reimposta tutto: reimposta HEAD su un altro commit, reimposta l'indice in modo che corrisponda e reimposta anche la directory di lavoro.

La differenza principale tra --mixede --softè se anche l'indice viene modificato. Scopri di più qui .


0

La risposta di mkarasek è fantastica, in termini semplici possiamo dire ...

  • git reset --soft: imposta il HEADcommit previsto ma mantieni le tue modifiche messe in scena dagli ultimi commit
  • git reset --mixed : è lo stesso di git reset --soft ma l'unica differenza è che mette in scena le modifiche apportate dagli ultimi commit
  • git reset --hard: imposta HEADil commit sull'impostazione specificata e reimposta tutte le modifiche degli ultimi commit, comprese le modifiche non impegnate.
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.