Reimpostazione, checkout e ripristino | Tutorial Atlassian Git

Reimpostazione, checkout e ripristino

I comandi git reset, git checkout e git revert sono alcuni degli strumenti Git disponibili più utili. Tutti ti consentono di annullare qualche tipo di modifica nel tuo repository e i primi due comandi possono essere usati per manipolare i commit o i singoli file.

Poiché sono così simili, è molto facile confondersi su quale comando usare in un determinato scenario di sviluppo. In questo articolo, confronteremo le configurazioni più comuni di git reset, git checkout e git revert. Lo scopo è darti la sicurezza necessaria per navigare nel tuo repository usando uno di questi comandi.

I tre alberi di Git

È utile pensare a ciascun comando in termini del suo effetto sui tre meccanismi di gestione dello stato di un repository Git: la directory di lavoro, l'istantanea in staging e la cronologia dei commit. Questi componenti sono talvolta noti come i "tre alberi" di Git. Esploriamo i tre alberi in modo approfondito nella pagina di git reset. Tieni a mente questi meccanismi mentre leggi questo articolo.

Un checkout è un'operazione che sposta il puntatore di riferimento HEAD su un commit specificato. Per dimostrarlo, considera il seguente esempio.

Sposta il puntatore HEAD ref su un commit specificato

Questo esempio dimostra una sequenza di commit sul branch main. Il riferimento HEAD e il riferimento del branch main puntano attualmente a commit d. Ora eseguiamo git checkout b

Sequenza di commit sul branch master

Si tratta di un aggiornamento dell'albero «Cronologia commit». Il comando git checkout può essere utilizzato in un commit o in un ambito a livello di file. Un checkout a livello di file cambierà il contenuto del file con quello del commit specifico.

Un ripristino è un'operazione che richiede un commit specificato e crea un nuovo commit che lo inverte. git revert può essere eseguito solo a livello di commit e non ha funzionalità a livello di file.

Un ripristino è un'operazione che richiede un commit specificato e reimposta i "tre alberi" in modo che corrispondano allo stato del repository in quel commit specificato. Un ripristino può essere richiamato in tre diverse modalità che corrispondono ai tre alberi.

Il checkout e il ripristino sono generalmente utilizzati per effettuare «annullamenti» locali o privati. Modificano la cronologia di un repository, il che può causare conflitti quando si esegue il push su repository condivisi remoti. Il ripristino è considerato un'operazione sicura per gli «annullamenti pubblici» in quanto crea una nuova cronologia che può essere condivisa in remoto e non sovrascrive la cronologia da cui i membri del team remoto possono dipendere.

Riferimenti Git Reset, Revert e Checkout a confronto

La tabella seguente riassume i casi d'uso più comuni per tutti questi comandi. Assicurati di tenere questo riferimento a portata di mano, poiché sicuramente dovrai usarne almeno alcuni durante la tua carriera con Git.

Comando Ambito Casi d'uso comuni
git reset Livello di commit Elimina i commit in un branch privato o eliminare le modifiche senza commit
git reset Livello di file Rimuovi lo stage di un file
Git checkout Livello di commit Passa da un branch all'altro o controlla le vecchie istantanee
Git checkout Livello di file Ignora le modifiche nella directory di lavoro
git revert Livello di commit Annulla i commit in un branch pubblico
git revert Livello di file (N/D)

Operazioni a livello di commit

I parametri che passi a git reset e git checkout ne determinano l'ambito. Quando non includi il percorso di un file come parametro, funzioneranno su commit interi. Questo è ciò che esploreremo in questa sezione. Nota che git revert non ha una controparte a livello di file.

Ripristinare un commit specifico

A livello di commit, il ripristino è un modo per spostare la punta di un branch su un altro commit. Può essere usato per rimuovere i commit dal branch corrente. Ad esempio, il comando seguente sposta il branch hotfix indietro di due commit.

git checkout hotfix git reset HEAD~2

I due commit alla fine dell'hotfix ora sono sospesi o orfani. Ciò significa che verranno eliminati la prossima volta che Git eseguirà una raccolta dei rifiuti. In altre parole, questi commit andranno scartati. Questo può essere visualizzato come segue:

Reimpostazione del branch hotfix su HEAD-2

Questo utilizzo di git reset è un modo semplice per annullare le modifiche che non sono state condivise con altri. È il comando ideale quando inizi a lavorare su una funzionalità e ti ritrovi a pensare: «Oh cavolo, cosa sto facendo? Dovrei semplicemente ricominciare da capo.»

Oltre a spostare il branch corrente, puoi anche far sì che git reset modifichi l'istantanea in staging e/o la directory di lavoro passando uno dei seguenti flag:

  • --soft: l'istantanea in staging e la directory di lavoro non vengono alterate in alcun modo.
  • --mixed: l'istantanea in staging viene aggiornata in modo da corrispondere al commit specificato, ma la directory di lavoro non è interessata. Questa è l'opzione predefinita.
  • --hard: l'istantanea in staging e la directory di lavoro sono entrambe aggiornate per corrispondere al commit specificato.

È più facile pensare a queste modalità come a un modo per definire l'ambito di un'operazione git reset. Per ulteriori informazioni dettagliate, visita la pagina di git resett.

Eseguire il checkout dei vecchi commit

Il comando git checkout viene utilizzato per aggiornare lo stato del repository a un punto specifico della cronologia dei progetti. Quando viene passato con il nome di un branch, ti consente di passare da un branch all'altro.

 git checkout hotfix

Internamente, tutto ciò che fa il comando precedente è spostare HEAD in un altro branch e aggiornare la directory di lavoro in modo che corrisponda. Poiché ciò può sovrascrivere le modifiche locali, Git ti obbliga a salvare o inserire in stash qualsiasi modifica nella directory di lavoro che andrà persa durante l'operazione di checkout. A differenza di git reset, git checkout non sposta alcun branch.

Spostamento di HEAD da master a hotfix

Puoi anche eseguire il checkout di commit arbitrari passando il riferimento al commit anziché a un branch. L'operazione è identica al checkout di un branch e sposta il riferimento HEAD nel commit specificato. Ad esempio, il seguente comando effettuerà il checkout del componente principale del commit corrente:

 git checkout HEAD~2
Spostamento di "HEAD" verso un commit arbitrario

Ciò è utile per ispezionare rapidamente una vecchia versione del tuo progetto. Tuttavia, poiché non esiste alcun riferimento branch all'HEAD attuale, entrerai in uno stato di HEAD distaccato. La cosa può rivelarsi pericolosa se inizi ad aggiungere nuovi commit perché non ci sarà modo di ripristinarli dopo il passaggio a un altro branch. Per questo motivo, dovresti sempre creare un nuovo branch prima di aggiungere commit a un HEAD distaccato.

Annullare i commit pubblici con Revert

Il comando Revert annulla un commit creandone uno nuovo. Questo è un modo sicuro per annullare le modifiche, in quanto non ha alcuna possibilità di riscrivere la cronologia dei commit. Ad esempio, il comando seguente calcolerà le modifiche contenute nel penultimo commit, creerà un nuovo commit annullando tali modifiche e aggiungerà il nuovo commit al progetto esistente.

git checkout hotfix git revert HEAD~2

Questo può essere visualizzato come segue:

Ripristino del penultimo commit

Confrontalo con git reset, che altera la cronologia dei commit esistente. Per questo motivo, git revert dovrebbe essere usato per annullare le modifiche su un branch pubblico e git reset dovrebbe essere riservato all'annullamento delle modifiche su un branch privato.

Puoi anche pensare a git revert come a uno strumento per annullare le modifiche con commit, mentre git reset HEAD serve per annullare le modifiche senza commit.

Come git checkout, git revert ha il potenziale per sovrascrivere i file nella directory di lavoro, quindi ti chiederà di eseguire il commit o inserire nello stash le modifiche che andrebbero perse durante l'operazione di ripristino.

Operazioni a livello di file

I comandi git reset e git checkout accettano anche un percorso di file opzionale come parametro, che altera drasticamente il loro comportamento. Invece di operare su intere istantanee, questo li obbliga a limitare le operazioni a un singolo file.

Git: reimpostare un file specifico

Quando viene richiamato con un percorso di file, git reset aggiorna l'istantanea in staging in modo che corrisponda alla versione del commit specificato. Ad esempio, questo comando recupererà la versione di foo.py nel penultimo commit e la preparerà per il commit successivo:

git reset HEAD~2 foo.py

Come con la versione di git reset a livello di commit, questa è più comunemente usata con HEAD piuttosto che con un commit arbitrario. L'esecuzione di git reset HEAD foo.py eliminerà lo staging di foo.py. Le modifiche che contiene saranno ancora presenti nella directory di lavoro.

Spostamento di un file dalla cronologia dei commit all'istantanea in staging

I flag --soft, --mixed e --hard non hanno alcun effetto sulla versione a livello di file di git reset, poiché l'istantanea in staging è sempre aggiornata e la directory di lavoro non viene mai aggiornata.

Git: eseguire il checkout di un file

Il checkout di un file è simile all'utilizzo di git reset con un percorso del file, tranne per il fatto che viene aggiornata la directory di lavoro anziché lo staging. A differenza della versione a livello di commit di questo comando, non viene spostato il riferimento HEAD, il che significa che non cambierai branch.

Spostamento di un file dalla cronologia dei commit alla directory di lavoro

Ad esempio, il comando seguente fa sì che foo.py nella directory di lavoro corrisponda a quello dal penultimo commit:

git checkout HEAD~2 foo.py

Proprio come l'invocazione a livello di commit di git checkout, può essere usata per ispezionare le vecchie versioni di un progetto, ma l'ambito è limitato al file specificato.

Se esegui lo staging ed esegui il commit del file con checkout, "ripristinerai" il file alla versione precedente. Verranno anche rimosse tutte le modifiche successive al file, mentre il comando git revert annulla solo le modifiche introdotte dal commit specificato.

Come git reset, è comunemente usato con HEAD come riferimento per il commit. Ad esempio, git checkout HEAD foo.py ha l'effetto di eliminare le modifiche senza staging a foo.py. È un comportamento simile a git reset HEAD --hard, ma funziona solo sul file specificato.

Riepilogo

Ora dovresti avere tutti gli strumenti di cui potresti aver bisogno per annullare le modifiche in un repository Git. I comandi git reset, git checkout e git revert possono creare confusione, ma se pensi ai loro effetti sulla directory di lavoro, sulle istantanee in staging e sulla cronologia dei commit, dovrebbe essere più facile capire quale comando si adatta al task di sviluppo in questione.