Guida

git checkout, switch e restore: quale usare e quando

Aggiornato: · Skillaria.Top

git checkout fa due cose diverse: cambia branch e ripristina file, per questo da Git 2.23 i suoi compiti sono divisi tra git switch (branch) e git restore (file). Per cambiare branch usate git switch nome, per annullare le modifiche non committate in un file git restore file, e lasciate checkout ai vecchi script e alle abitudini consolidate.

Perché git checkout ha due ruoli

Storicamente git checkout svolge due operazioni profondamente diverse. Con il nome di un branch sposta HEAD e aggiorna la working directory. Con il percorso di un file prende la versione del file dall'indice o da un commit e sovrascrive in silenzio le vostre modifiche. Lo stesso comando ora passa in sicurezza da un branch all'altro, ora butta via in modo irreversibile il lavoro non salvato.

Per questo in Git 2.23 sono comparsi due comandi separati: git switch per i branch e git restore per i file. Checkout non è stato eliminato e funziona perfettamente, ma nel codice recente, nella documentazione e nei suggerimenti di Git stesso si usano switch e restore. Se state imparando, partite da questi; checkout va comunque conosciuto, perché compare in guide, script e colloqui.

Corrispondenza tra i comandi

Operazionegit checkoutNuovo comando
Passare a un branchgit checkout maingit switch main
Creare un branch e spostarsigit checkout -b featuregit switch -c feature
Creare o reimpostare un branchgit checkout -B featuregit switch -C feature
Tornare al branch precedentegit checkout -git switch -
Spostarsi su un commit (detached HEAD)git checkout 3f2a1c9git switch --detach 3f2a1c9
Annullare le modifiche a un filegit checkout -- app.jsgit restore app.js
Togliere un file dall'indicegit reset HEAD app.jsgit restore --staged app.js
Prendere un file da un altro branch o commitgit checkout main -- app.jsgit restore --source=main --staged --worktree app.js
Annullare tutte le modifiche nella cartellagit checkout -- .git restore .

Notate il -- nei comandi checkout. È il separatore «da qui in poi ci sono percorsi». Protegge dall'ambiguità quando un file e un branch hanno lo stesso nome. In restore il problema non esiste, perché il comando lavora solo con i file.

git checkout per i branch

git checkout feature/login        # cambiare branch
git checkout -b feature/login     # creare dal commit corrente e spostarsi
git checkout -b hotfix origin/main   # creare da un branch remoto
git checkout -t origin/feature/x  # branch locale che traccia quello remoto

Se il branch locale non esiste ma sul server c'è un branch con quel nome, git checkout feature/x (come git switch feature/x) crea da solo il branch locale e configura il tracking. Prima serve git fetch, altrimenti Git non sa del nuovo branch remoto.

Il cambio di branch non perde le modifiche non committate: vengono portate nell'altro branch se non sono in conflitto con esso. In caso di conflitto Git rifiuta e propone di fare commit o di mettere da parte le modifiche con git stash. L'opzione -f (--force) cambia branch buttando via le modifiche locali: è già un'operazione pericolosa.

git switch: solo branch

Switch ripete la parte di checkout dedicata ai branch, ma con regole più severe. La differenza principale: non si sposta su un commit o un tag senza un esplicito --detach. Il comando git switch v1.0 dà errore invece di passare silenziosamente in detached HEAD, quindi è più difficile ritrovarsi per sbaglio «fuori da un branch».

git switch main
git switch -c feature/cart
git switch -c feature/cart origin/main
git switch --detach v1.0
git switch -              # branch precedente
git switch -m main        # portare le modifiche con un merge a tre vie

git restore: il pulsante annulla per i file

Restore riporta i file a uno stato salvato e non tocca in alcun modo la storia dei commit. Per usarlo con sicurezza bisogna ricordare le tre aree di Git: la working directory (ciò che vedete nell'editor), l'indice (ciò che avete preparato con git add) e i commit.

Come annullare le modifiche a un file prima di git add

git restore app.js         # ripristinare la versione dell'indice
git restore src/           # tutti i file nella cartella
git restore .              # tutti i file modificati del progetto

Senza opzioni restore ripristina la working directory dall'indice. Se il file non è stato aggiunto all'indice dopo l'ultimo commit, equivale alla versione dell'ultimo commit. Le modifiche spariscono senza lasciare traccia, non finiscono nel cestino, quindi prima di git restore . date un'occhiata a git diff.

Come togliere un file dall'indice dopo git add

git restore --staged app.js

Il file non è più pronto per il commit, ma le vostre modifiche restano. È la forma più sicura di restore: non si perde nulla. È proprio il comando che suggerisce git status nella sezione «Changes to be committed».

Come annullare tutto: sia nell'indice sia nel file

git restore --staged --worktree app.js
# forma breve
git restore -SW app.js

Come recuperare un file da un altro commit o branch

git restore --source=HEAD~2 app.js          # la versione di due commit fa
git restore --source=main config.yml        # la versione del branch main
git restore --source=3f2a1c9 -- docs/       # un'intera cartella da un commit

Di default con --source cambia solo la working directory. Aggiungete --staged se volete preparare subito la modifica per il commit. La vecchia variante git checkout main -- app.js modifica sempre sia l'indice sia la working directory.

Come ripristinare un file eliminato

git restore deleted.txt                          # eliminato ma non ancora committato
git restore --source=HEAD~1 deleted.txt          # l'eliminazione è già committata

Se non sapete in quale commit il file è stato eliminato, trovatelo: git log --oneline -- deleted.txt. L'ultimo commit dell'elenco è quello in cui il file è stato eliminato, e va ripristinato dal suo genitore: git restore --source=<hash>~1 deleted.txt.

restore, reset e revert: non confonderli

Tutti e tre «annullano», ma a profondità diverse:

  • restore: lavora con i file nella working directory e nell'indice, non cambia la storia. Sicuro per il team.
  • reset: sposta indietro il puntatore del branch, riscrivendo la storia. --soft lascia le modifiche nell'indice, --mixed (predefinito) nella working directory, --hard le elimina. Pericoloso per commit già inviati su branch condivisi.
  • revert: crea un nuovo commit opposto a quello indicato. La storia non viene riscritta, quindi è il modo corretto di annullare un commit già sul server.

Regola semplice: modifiche non ancora committate, restore; commit solo locale, reset; commit già presso i colleghi, revert.

Detached HEAD dopo checkout

Eseguendo git checkout 3f2a1c9 o git checkout v1.0 vedrete un lungo avviso «You are in 'detached HEAD' state». Ora HEAD punta a un commit, non a un branch. È comodo per guardare una versione vecchia o lanciarci i test.

In questo stato si può fare commit, ma i nuovi commit non finiranno in nessun branch. Se avete modificato qualcosa e volete conservarlo:

git switch -c fix-for-v1

Se stavate solo guardando, tornate indietro: git switch - o git switch main. Se ve ne siete andati dimenticando di creare un branch, i commit sono ancora accessibili tramite git reflog: trovate l'hash ed eseguite git branch rescue <hash>.

Errori comuni

error: pathspec 'feature' did not match any file(s) known to git

Git non ha trovato né un branch né un file con quel nome. Di solito il branch esiste solo sul server e non è stato fatto il fetch. Eseguite git fetch e riprovate. Controllate anche l'ortografia: i nomi dei branch distinguono tra maiuscole e minuscole.

Ho buttato via le modifiche per errore con checkout -- o restore

Le modifiche non committate che non erano nell'indice Git non le conserva, e con gli strumenti di Git non si possono recuperare. Resta la cronologia locale dell'editor o dell'IDE. Se invece il file era stato aggiunto con git add, il suo contenuto si trova nel database come oggetto «pendente» e si recupera con git fsck --lost-found.

File e branch con lo stesso nome

git checkout test, se esistono un branch test e una cartella test, mostra un avviso di ambiguità. Usate git switch test per il branch e git restore test (o git checkout -- test) per i file.

Scenari di lavoro quotidiani

Alcune situazioni che capitano di continuo e il comando giusto per ciascuna:

  • Avete iniziato a modificare un file e capito di aver preso la strada sbagliata: git restore file.
  • Avete aggiunto all'indice un file di troppo prima del commit: git restore --staged file.
  • Dovete passare d'urgenza a un altro task nel bel mezzo del lavoro: git stash, poi git switch branch; al ritorno, git switch - e git stash pop.
  • Volete prendere un solo file da un branch vicino senza unire tutto il branch: git restore --source=feature/x --staged --worktree percorso.
  • Dovete verificare se il bug c'era nella versione precedente: git switch --detach v1.0, verifica, poi git switch -.
  • Volete annullare solo una parte delle modifiche in un file: git restore -p file; Git mostrerà ogni blocco e chiederà se annullarlo.

La modalità interattiva -p (--patch) è particolarmente utile: esiste sia in checkout sia in restore e permette di eliminare l'output di debug mantenendo le modifiche utili nello stesso file.

Come controllare il risultato prima di annullare

Restore e checkout con percorsi buttano via le modifiche senza conferma, quindi conviene prima guardare cosa andrà perso. git diff mostra le modifiche nella working directory rispetto all'indice, git diff --staged ciò che è pronto per il commit, git diff main -- app.js in cosa il file differisce dalla versione in un altro branch. Nel dubbio, salvate lo stato attuale con git stash: da lì le modifiche si possono sempre recuperare, a differenza di restore.

In sintesi

git checkout unisce il cambio di branch e il ripristino dei file, ed è proprio questo a renderlo pericoloso. Per i branch usate git switch, per i file git restore: un comando cambia solo la posizione di HEAD, l'altro solo il contenuto dei file. Branch, restore, reset e revert, con scenari buoni e cattivi, sono trattati in pratica nel corso su Git.

Domande frequenti

git checkout è deprecato?

No, non è stato dichiarato deprecato e continuerà a funzionare. Semplicemente per il lavoro quotidiano si consigliano git switch e git restore, più specifici e più sicuri.

Da quale versione di Git esistono switch e restore?

Entrambi i comandi sono comparsi in Git 2.23 (2019). La versione si controlla con git --version.

Si può annullare git restore?

Se è stata ripristinata la working directory, Git non ha conservato le modifiche scartate e non si può annullare. git restore --staged è sicuro: le modifiche restano nel file, basta aggiungerle di nuovo con git add.

Come passo al branch di un collega?

Eseguite git fetch, poi git switch nome-branch: Git creerà un branch locale collegato a origin/nome-branch.

Che differenza c'è tra git checkout . e git reset --hard?

git checkout . (o git restore .) riporta i file modificati alla versione dell'indice, senza toccare indice e storia. git reset --hard reimposta sia l'indice sia la working directory al commit e, con un commit come argomento, sposta anche il branch.

Impara l’argomento in video

Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».

Vai al corso