File .gitignore: come escludere i file da Git e rimuovere quelli già aggiunti
Il file .gitignore nella radice del repository elenca i modelli di file e cartelle che Git non tracerà: dipendenze, build, log, segreti. Vale solo per i file non tracciati, quindi un file già committato va tolto dall'indice con git rm --cached.
Il meccanismo di gitignore serve a dire a Git: «questi file non toccarli, non mostrarli in git status e non aggiungerli con git add .». Le regole si scrivono in un file di testo chiamato .gitignore, che viene committato insieme al progetto e vale per tutto il team. La sottigliezza principale: il file di esclusione agisce solo sui file che Git non traccia ancora.
Che cosa si aggiunge di solito a .gitignore
- Dipendenze:
node_modules/,vendor/,.venv/— le ripristina il gestore dei pacchetti. - Risultati della build:
dist/,build/,*.class,__pycache__/. - Log e file temporanei:
*.log,tmp/,*.swp. - Segreti e impostazioni locali:
.env, chiavi, configurazioni locali. - Rifiuti di sistema e dell'editor:
.DS_Store,Thumbs.db,.idea/— meglio però tenerli nel gitignore globale, di cui parliamo più avanti.
La regola è semplice: nel repository deve finire ciò che scrivono le persone, non ciò che generano i programmi o che dipende dalla singola macchina.
Come creare un .gitignore
È un normale file di testo nella radice del repository. Createlo, scrivete i modelli uno per riga e committatelo:
cat > .gitignore <<'EOF'
# dipendenze
node_modules/
# build
dist/
# log
*.log
# segreti
.env
EOF
git add .gitignore
git commit -m "chore: aggiunto .gitignore"
Modelli pronti per i linguaggi e i framework più diffusi si trovano nel repository github/gitignore; GitHub e GitLab propongono di sceglierne uno quando create il progetto. Usatelo come base e togliete ciò che non serve.
Sintassi dei modelli .gitignore
| Modello | Che cosa ignora |
|---|---|
*.log | tutti i file con estensione .log in qualsiasi cartella |
build/ | la cartella build a qualsiasi livello (la barra finale indica solo le cartelle) |
/build | solo build nella radice (la barra iniziale lega il modello alla cartella del .gitignore) |
docs/*.pdf | i pdf direttamente in docs, ma non in docs/old/ |
docs/**/*.pdf | i pdf in docs e in tutte le sottocartelle |
**/temp | temp in qualsiasi cartella |
!keep.log | eccezione: non ignorare keep.log |
file?.txt | ? — un carattere qualsiasi |
[ab].txt | a.txt oppure b.txt |
# commento | riga di commento |
Dettaglio importante: un modello senza barra all'interno (config.json) vale a qualsiasi livello, mentre un modello con una barra all'inizio o nel mezzo (/config.json, src/config.json) è riferito alla cartella in cui si trova questo .gitignore.
Eccezioni con il punto esclamativo
*.log
!important.log
Trappola: non si può «riabilitare» un file se è ignorata la sua cartella madre. Git non entra nelle cartelle ignorate, quindi questo non funziona:
logs/
!logs/keep.txt # non funziona
Il modo giusto è ignorare il contenuto, non la cartella:
logs/*
!logs/keep.txt
Lo stesso trucco serve a conservare una cartella vuota nel repository: Git non memorizza le cartelle vuote, quindi si mette dentro un file .gitkeep e si consente solo quello.
Dove altro si può impostare l'esclusione
| Posizione | Vale per | Finisce nel repository |
|---|---|---|
.gitignore nella radice e nelle sottocartelle | tutto il team | sì |
.git/info/exclude | solo questo clone | no |
file globale indicato in core.excludesFile | tutti i vostri repository | no |
I rifiuti del vostro sistema operativo e dell'IDE conviene metterli nel gitignore globale, per non sporcare quello condiviso:
git config --global core.excludesFile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global
echo ".idea/" >> ~/.gitignore_global
Se core.excludesFile non è impostato, Git legge ~/.config/git/ignore, se esiste.
Perché .gitignore non funziona
La lamentela più frequente è: «ho aggiunto il file a .gitignore, ma Git mostra ancora le modifiche». La causa è quasi sempre una sola: il file è già tracciato. L'esclusione non ha effetto sui file che sono finiti in un commit.
Per capire quale regola si applica (o perché non si applica) serve git check-ignore:
git check-ignore -v logs/app.log
# .gitignore:3:*.log logs/app.log
L'output indica il file e la riga della regola. Un output vuoto significa che il file non è ignorato. Un'altra causa di problemi sono gli spazi alla fine di una riga del modello o una codifica sbagliata del file (per esempio UTF-16, se creato con PowerShell).
Come togliere dal repository un file già committato: git rm --cached
Il comando git rm rimuove il file dall'indice e dal disco. Con il flag --cached lo rimuove solo dall'indice: il file resta sul vostro computer, ma smette di essere tracciato.
echo ".env" >> .gitignore
git rm --cached .env
git commit -m "chore: .env non è più tracciato"
git push
Per una cartella serve -r:
git rm -r --cached node_modules/
Se le regole sono molte e non è chiaro che cosa sia già finito nel repository, si può ricostruire l'indice da zero: git rm -r --cached ., poi git add . e il commit. Prima di committare controllate git status: vi compariranno i file tolti dall'indice.
Attenzione: i segreti restano nella cronologia
git rm --cached toglie il file dai commit successivi, ma non da quelli precedenti. Se nel repository è finita una password o un token, è accessibile nella cronologia a chiunque abbia un clone. Il segreto va considerato compromesso e rigenerato. La cronologia si può riscrivere con strumenti come git filter-repo, ma così cambiano gli hash di tutti i commit successivi e bisogna concordarlo con il team.
Un'altra conseguenza: ai colleghi, dopo un git pull, un file tolto dall'indice con --cached verrà eliminato dal disco. Per il file .env avvisate il team in anticipo oppure tenete nel repository un modello .env.example.
git rm: eliminare i file
git rm old.py # elimina dal disco e dall'indice
git rm -r legacy/ # elimina una cartella
git rm -n '*.tmp' # dry run: mostra che cosa verrebbe eliminato
git rm -f changed.py # elimina un file con modifiche non committate
Se eliminate un file con il normale rm, Git lo vede come «deleted» e la cancellazione va aggiunta con git add o git rm. git rm fa entrambi i passi in un colpo solo. Senza -f rifiuta di eliminare un file con modifiche non committate: è una protezione contro la perdita di lavoro.
git mv: rinominare e spostare
git mv utils.py helpers.py
git mv helpers.py src/helpers.py
git mv è l'abbreviazione di mv + git rm del vecchio percorso + git add del nuovo. Git non memorizza il fatto che un file è stato rinominato: lo deduce dalla somiglianza del contenuto quando mostra git status, git log --follow o git diff. Quindi rinominare con git mv oppure con il normale mv seguito da git add -A dà lo stesso risultato.
Consiglio pratico: rinominate e modificate il contenuto del file in commit diversi. Se il file cambia molto insieme al nome, Git potrebbe non riconoscerlo come lo stesso file e la cronologia con --follow si interrompe.
Per cambiare solo le maiuscole/minuscole del nome su Windows e macOS (i file system non distinguono le maiuscole) usate git mv readme.md README.md: una semplice rinomina in Esplora risorse potrebbe non essere notata da Git.
Ordine delle regole e priorità
All'interno di un file le regole si leggono dall'alto verso il basso e vince l'ultima che corrisponde. Per questo l'eccezione !keep.log deve stare sotto la regola *.log, altrimenti viene coperta. Tra file diversi la priorità è questa: il .gitignore di una sottocartella prevale su quello della radice, mentre il gitignore globale e .git/info/exclude hanno la priorità più bassa.
Esempio di .gitignore per un progetto web
Ecco una versione funzionante per un progetto con Node.js e un backend in Python. Leggete i commenti: tra sei mesi nessuno ricorderà perché è stata aggiunta una certa regola.
# --- dipendenze ---
node_modules/
.venv/
# --- build e cache ---
/dist
/build
__pycache__/
*.pyc
.pytest_cache/
coverage/
# --- log ---
*.log
npm-debug.log*
# --- ambiente e segreti ---
.env
.env.*
!.env.example
# --- upload degli utenti: la cartella resta, il contenuto no ---
uploads/*
!uploads/.gitkeep
Qui si usano entrambi i trucchi con le eccezioni: .env.* nasconde tutte le varianti locali dell'ambiente, ma il modello .env.example resta nel repository; la cartella uploads esiste nel clone grazie al file .gitkeep.
Che cosa non conviene ignorare
Anche esagerare con le esclusioni fa male. I file di lock delle dipendenze — package-lock.json, poetry.lock, composer.lock — nelle applicazioni si committano di solito: garantiscono che tutti gli sviluppatori e il CI installino le stesse versioni dei pacchetti. Anche le impostazioni di formattazione condivise (.editorconfig, configurazioni dei linter) devono stare nel repository.
Caso controverso: le impostazioni dell'IDE. La cartella .vscode/ viene spesso ignorata per intero, ma a volte si committano le estensioni consigliate e le attività di build: .vscode/*, poi !.vscode/extensions.json. La decisione spetta al team; l'importante è non mettere nel repository condiviso percorsi personali e chiavi.
Scenario: avete committato node_modules per errore
Errore classico di chi comincia: git add . prima di creare il .gitignore, e nel commit finiscono decine di migliaia di file. La correzione in tre passi:
echo "node_modules/" >> .gitignore
git rm -r --cached node_modules
git commit -m "chore: node_modules rimosso dal repository"
Se il commit non è ancora stato inviato al server, conviene riscriverlo per non lasciare i file pesanti nella cronologia: git reset --soft HEAD~1, poi gli stessi comandi e un nuovo commit. Dopo il push i file sono già nella cronologia, e la dimensione del clone non diminuirà senza riscrivere la storia.
Per non ripetere l'errore, create il .gitignore prima del primo git add e abituatevi a guardare git status prima di committare: un elenco di nuovi file sospettosamente lungo è il motivo per fermarsi.
Riepilogo
Il file .gitignore è il primo file da committare in un progetto nuovo: dipendenze, build, log e segreti non devono stare nel repository. L'esclusione funziona solo sui file non tracciati, quindi quelli già aggiunti si tolgono con git rm --cached, e le regole si verificano con git check-ignore -v. Per eliminare e rinominare usate git rm e git mv, così le modifiche entrano subito nell'indice. Il lavoro con l'indice e il primo repository lo analizziamo in pratica nel corso di Git.
Domande frequenti
Bisogna committare anche il file .gitignore?
Sì, così le regole valgono per tutto il team; le esclusioni personali mettetele in .git/info/exclude oppure nel gitignore globale.
Si possono avere più file .gitignore?
Sì, il file può stare in qualsiasi sottocartella e le sue regole valgono rispetto a quella cartella, integrando quelle della radice.
Come si aggiunge al commit un file ignorato?
Usate git add -f percorso/del/file; dopo questo il file è tracciato e .gitignore non lo riguarda più.
Come vedere tutti i file ignorati del progetto?
Eseguite git status --ignored oppure git ls-files --others --ignored --exclude-standard.
Come smettere temporaneamente di tracciare modifiche a un file già committato?
Esiste git update-index --skip-worktree file, ma è un'impostazione locale e non sostituisce .gitignore.
Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».
Vai al corso