Guida

git add e git commit: come salvare correttamente le modifiche

Aggiornato: · Skillaria.Top

In Git si salvano le modifiche in due passaggi: git add aggiunge le modifiche scelte all'indice (staging area) e git commit -m "messaggio" le registra nella storia come un'unica istantanea. git status mostra cosa è cambiato e cosa è già pronto. L'ultimo commit si corregge con git commit --amend.

In Git non esiste un pulsante «salva». Le modifiche si registrano in due passaggi: prima git add seleziona quali modifiche entreranno nella prossima istantanea, poi git commit scrive quell'istantanea nella storia. La zona intermedia tra i due si chiama indice, o staging area. È proprio la parte che molti non capiscono per anni, ed è proprio quella che rende i commit sensati.

I tre stati di un file: working directory, indice, storia

Ogni modifica attraversa tre aree:

  1. Working directory: modificate il file nell'editor.
  2. Indice (stage): git add mette la versione attuale del file nella bozza del prossimo commit.
  3. Storia: git commit trasforma il contenuto dell'indice in un commit con autore, data e messaggio.

Un'analogia comoda è un pacco. La working directory è la stanza piena di oggetti, l'indice è la scatola aperta in cui mettete ciò che volete spedire, e il commit è il pacco sigillato ed etichettato. Siete voi a decidere cosa mettere nella scatola, e non siete obbligati a spedire tutto in una volta.

A cosa serve l'indice

Immaginate: in un'ora avete corretto un bug nell'autenticazione, sistemato un refuso nel README e iniziato una nuova funzionalità. Senza indice tutto finirebbe in un unico commit «varie». Con l'indice fate tre commit separati, ognuno facile da capire, da controllare in code review e, se serve, da annullare senza toccare il resto.

git status: cosa è cambiato e cosa è pronto

Prima di ogni add e commit conviene guardare git status. È il comando più sicuro di Git: non modifica nulla.

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   src/auth.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        src/profile.js
  • Changes to be committed: già nell'indice, entrerà nel commit;
  • Changes not staged: modificato ma non aggiunto;
  • Untracked files: file nuovi che Git non conosce ancora.

Il formato breve è più comodo quando i file sono tanti:

$ git status -s
M  src/auth.js
 M README.md
?? src/profile.js

La prima colonna è lo stato nell'indice, la seconda nella working directory. M a sinistra significa «modificato e aggiunto», a destra «modificato, non aggiunto», ?? indica un file non tracciato.

git add: come aggiungere le modifiche all'indice

ComandoCosa aggiunge
git add file.jsUn file specifico
git add src/Tutto ciò che è cambiato nella cartella
git add .Tutte le modifiche nella cartella corrente e nelle sottocartelle, inclusi file nuovi ed eliminati
git add -ATutte le modifiche nell'intero repository
git add -uSolo i file già tracciati, ignora quelli nuovi
git add -pIn modo interattivo, a blocchi (hunk)
git add '*.css'File che corrispondono a un pattern

Un sinonimo di git add è git stage: è lo stesso comando, da cui il termine «stage» e l'espressione «mettere in stage».

git add -p: aggiungere solo una parte del file

È la tecnica più sottovalutata. Se in un file sono mescolate due modifiche diverse, git add -p mostra ogni blocco e chiede se aggiungerlo:

$ git add -p src/auth.js
@@ -12,7 +12,7 @@ function login(user) {
-  if (user.password = input) {
+  if (user.password === input) {
(1/2) Stage this hunk [y,n,q,a,d,s,e,?]?

y aggiunge, n salta, s divide il blocco in parti più piccole, q esce. Così da un solo file si ottengono due commit puliti.

Come verificare cosa finirà nel commit

git diff            # modifiche NON ancora aggiunte all'indice
git diff --staged   # ciò che è già nell'indice ed entrerà nel commit

Un equivoco frequente: dopo git add il semplice git diff non mostra nulla. Non significa che le modifiche siano sparite: sono nell'indice, guardatele con git diff --staged (sinonimo di --cached).

Come togliere un file dall'indice

git restore --staged file.js

Le modifiche nel file restano: semplicemente il file non è più pronto per il commit. Nelle vecchie versioni di Git si usava git reset HEAD file.js, che funziona ancora oggi.

git commit: registrare le modifiche

git commit -m "Correggi il controllo della password al login"
[main 3f2a9c1] Correggi il controllo della password al login
 1 file changed, 1 insertion(+), 1 deletion(-)

La risposta riporta il branch, l'hash breve del nuovo commit e le statistiche. Nel commit entra solo il contenuto dell'indice. Se dopo git add avete modificato di nuovo il file, le nuove modifiche non entreranno nel commit finché non le aggiungerete di nuovo.

Opzioni utili di git commit

ComandoCosa fa
git commitApre l'editor per un messaggio su più righe
git commit -m "..."Messaggio direttamente nel comando
git commit -am "..."Aggiunge tutti i file tracciati modificati e fa il commit; i file nuovi non entrano
git commit -vMostra il diff nell'editor mentre scrivete il messaggio
git commit --amendRiscrive l'ultimo commit
git commit --allow-empty -m "..."Commit vuoto, ad esempio per far ripartire la CI

Se l'editor predefinito è Vim e ci siete rimasti bloccati: scrivete il messaggio, premete Esc, poi digitate :wq e Invio. Per cambiare editor: git config --global core.editor "code --wait" oppure nano.

Come scrivere un buon messaggio di commit

Il messaggio verrà letto tra sei mesi, quando qualcuno cercherà da dove arriva un bug. Deve rispondere a «cosa e perché», non a «cosa ho fatto oggi».

  • prima riga di 50–72 caratteri al massimo, con l'essenza della modifica;
  • all'imperativo o in forma neutra: «Aggiungi la validazione dell'email», «Fix login redirect»;
  • se serve, una riga vuota e i dettagli: perché così, quali limiti ci sono;
  • un commit, una modifica logica.

Male: fix, modifiche, wip, asdf. Bene: Correggi il crash del carrello con lista prodotti vuota. Molti team usano il formato Conventional Commits: feat: ..., fix: ..., docs: ....

git commit --amend: come correggere l'ultimo commit

Un refuso nel messaggio o un file dimenticato: non serve creare un nuovo commit «file dimenticato». Per questo esiste amend.

Correggere il messaggio

git commit --amend -m "Correggi il controllo della password al login"

Aggiungere un file dimenticato

git add forgotten.js
git commit --amend --no-edit

L'opzione --no-edit mantiene il messaggio precedente.

Cosa sapere su amend

--amend non «modifica» il commit: ne crea uno nuovo con un hash diverso e sostituisce il vecchio. Finché il commit esiste solo in locale è sicuro. Se avete già fatto git push, dopo amend il push normale verrà rifiutato e dovrete usare git push --force-with-lease. Nei branch condivisi, dove lavorano altri, è meglio non farlo: riscrivereste la storia su cui stanno già costruendo il proprio lavoro.

Se avete fatto amend per errore, il vecchio commit si trova in git reflog e ci si torna con git reset --soft HEAD@{1}.

Errori comuni

«nothing to commit, working tree clean»

Non ci sono modifiche: è già tutto nel commit, oppure state modificando un file in un'altra cartella. Controllate git status e il percorso del progetto.

«no changes added to commit»

I file sono modificati ma non aggiunti all'indice. Eseguite git add oppure usate git commit -am per i file già tracciati.

«Please tell me who you are»

Nome ed e-mail non sono impostati. Eseguite una volta git config --global user.name "Nome" e git config --global user.email "email".

Nel commit è finito qualcosa di troppo

Se il commit non è ancora stato inviato, annullatelo mantenendo le modifiche nell'indice: git reset --soft HEAD~1. Poi togliete il superfluo con git restore --staged e rifate il commit. Se nel commit è finito un segreto, consideratelo compromesso e rigeneratelo.

git add . ha aggiunto file spazzatura

Configurate .gitignore in anticipo e controllate git status prima del commit. Un file già tracciato smette di esserlo solo dopo git rm --cached file.

Che cos'è davvero un commit

Un commit non conserva la differenza rispetto alla versione precedente, ma un'istantanea dell'intero progetto: un riferimento all'albero dei file, un riferimento al commit padre, l'autore, la data e il messaggio. I file non modificati non vengono copiati: Git rimanda agli oggetti già salvati, per questo la storia occupa poco spazio.

L'hash del commit (ad esempio 3f2a9c1) è calcolato da tutto questo contenuto. Cambiate anche una sola lettera nel messaggio e otterrete un hash diverso. Ecco perché --amend e rebase creano nuovi commit invece di modificare i vecchi, e perché una storia riscritta va in conflitto con le copie dei colleghi.

Per vedere la storia in forma compatta:

git log --oneline --graph -10

Ogni riga è un commit; se i messaggi della vostra storia si capiscono senza aprire il diff, state lavorando bene.

Il ciclo di lavoro quotidiano

git status                 # cosa è cambiato
git diff                   # guardare le modifiche
git add -p                 # selezionare ciò che serve
git diff --staged          # verificare cosa entrerà nel commit
git commit -m "Descrizione chiara"
git log --oneline -5       # controllare che il commit ci sia
git push                   # inviare al server

Il commit salva le modifiche solo in locale. Perché arrivino su GitHub o su un altro server serve git push.

In sintesi

git add prepara la prossima istantanea nell'indice, git status e git diff --staged mostrano cosa ne farà parte, e git commit registra l'istantanea nella storia. Fate commit piccoli e logici con messaggi chiari, e per piccole correzioni all'ultimo commit usate git commit --amend, prima del push. L'indice e lo schema in due passaggi add → commit sono trattati in pratica nel corso su Git.

Domande frequenti

Ogni quanto bisogna fare commit?

Ogni volta che una modifica logica è completa e il progetto funziona. Meglio tanti commit piccoli che uno enorme a fine giornata.

Esiste in Git un comando git save?

No, questo comando non esiste. Salvare significa git add e git commit, mentre il lavoro non finito si può mettere da parte temporaneamente con git stash.

Come vedo cosa contiene un commit?

Il comando git show hash mostra autore, messaggio e tutte le modifiche del commit. Senza hash mostra l'ultimo commit.

Che differenza c'è tra git add . e git add -A?

Nelle versioni moderne di Git entrambi aggiungono file nuovi, modificati ed eliminati. La differenza è l'ambito: git add . lavora sulla cartella corrente e sulle sottocartelle, git add -A sull'intero repository.

Si può modificare un commit più vecchio, non l'ultimo?

Sì, con il rebase interattivo: git rebase -i hash-del-padre, poi si segna il commit desiderato come edit o reword. Fatelo solo con commit non ancora inviati a un branch condiviso.

Impara l’argomento in video

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

Vai al corso