git checkout, switch e restore: quale usare e quando
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
| Operazione | git checkout | Nuovo comando |
|---|---|---|
| Passare a un branch | git checkout main | git switch main |
| Creare un branch e spostarsi | git checkout -b feature | git switch -c feature |
| Creare o reimpostare un branch | git checkout -B feature | git switch -C feature |
| Tornare al branch precedente | git checkout - | git switch - |
| Spostarsi su un commit (detached HEAD) | git checkout 3f2a1c9 | git switch --detach 3f2a1c9 |
| Annullare le modifiche a un file | git checkout -- app.js | git restore app.js |
| Togliere un file dall'indice | git reset HEAD app.js | git restore --staged app.js |
| Prendere un file da un altro branch o commit | git checkout main -- app.js | git restore --source=main --staged --worktree app.js |
| Annullare tutte le modifiche nella cartella | git 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.
--softlascia le modifiche nell'indice,--mixed(predefinito) nella working directory,--hardle 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, poigit switch branch; al ritorno,git switch -egit 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, poigit 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.
Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».
- Branch: la realtà parallela del tuo codice
- Annullare gli errori: restore, reset, revert — correttamente e con cautela