Nelle precedenti lezioni abbiamo visto come salvare degli snapshot all’interno del repository. Ogni volta che abbiamo aggiunto un nuovo file o modificato uno già esistente, Git si accorgeva delle modifiche apportate e ci segnalava quali file potevano essere inseriti nella Staging Area e quindi all’interno del prossimo commit. Ci sono però alcune situazioni in cui è necessario ignorare dei file e fare in modo che Git non tenga traccia di eventuali modifiche. Alcuni possibili casi sono:
- cartelle create da un package manager come NPM o bower
- directory contenenti i file da caricare su un server per un’applicazione web
- File di supporto generati dal sistema operativo o da altri strumenti
- file eseguibili generati a partire dal codice sorgente
- archivi compressi come file.gz o.zip
Il file .gitignore
Per selezionare dei file da ignorare in un determinato progetto, possiamo creare un file .gitignore nella cartella base. (Sarebbe possibile inserire più file.gitignore in cartelle diverse ma solitamente si usa un file solo).
All’interno del file .gitignore inseriremo su ogni riga le regole che Git deve seguire per capire quale file ignorare o meno. È possibile limitarsi a elencare una lista di file o usare delle espressioni regolari. Per inserire dei commenti nel file .gitignore basta iniziare una riga con il carattere ‘#’.
Vediamo un semplice esempio.
# Esempio di regole da inserire in un file .gitignore
# Con il carattere cancelletto si indica l'inizio di una riga di commento
# ignora il file file_da_ignorare.txt
# ignora anche nome_cartella/file_da_ignorare.txt
file_da_ignorare.txt
# ignora il file file_da_ignorare.txt solo all'interno della directory base
# NON ignora nome_cartella/file_da_ignorare.txt
/file_da_ignorare.txt
# ? corrisponde a un singolo carattere
# Vengono quindi ignorati file del tipo
# file1.txt, fileA.txt, nome_cartella/file1.txt
file?.txt
# [set_di_caratteri] per specificare un set di caratteri
# Vengono ignorati i file file_a.txt e file_b.txt, ma non file_c.txt
# nella cartella base o in una sottocartella
file_[ab].txt
# [A-Z] per specificare un range di caratteri
# vengono ignorati i file com fooA.txt e fooE.txt
foo[A-Z].txt
# vengono ignorati i file come foo0.txt e foo2.txt
foo[0-9].txt
# il carattere * effettua il match di zero o più caratteri
# vengono ignorati i file come archivio.zip o cartella/archivio.zip
*.zip
# il carattere ! nega il pattern specificato
# i file che per qualche regola precedente vengono ignorati,
# vengono nuovamente inclusi
# Per esempio 'archivio_speciale.zip' non verrà ignorato più
!archivio_speciale.zip
# usando due asterischi **, viene eseguito il match di una directory
# nella cartella base o qualsiasi altra cartella del progetto
# La regola seguente ignora file come:
#
# images/img.jpeg
# images/december/snow.png
# asset/images/example.gif
**/images
# se non si inserisce la slash finale
# vengono ignorati file e il contenuto delle cartelle
# aventi quel nome
# La regola sottostante ignora:
# asset
# asset/esempio.png
# asset/images/immagine.jpeg
# dicembre/asset
# dicembre/asset/immagine_1.gif
asset
# Usando la slash finale si indica che si tratta di una directory
# La regola seguente ignora file del tipo:
# asset/img.jpeg
# asset/images/file.png
# dicembre/asset/immagine.png
# dicembre/asset/immagini/a.webp
asset/
# La regola seguente ignora il file todos.txt nella cartella tasks
tasks/todos.txt
# ignora TUTTI i file nella directory build
# non è possibile includere nuovamente un file se la directory in cui
# è contenuto viene ignorata
build/
# Il doppio asterisco ** esegue il match di zero o più directory
logs/**/error.log
Una volta inserite tutte le regole necessarie, possiamo effettuare il commit del file .gitignore, aggiungerlo nel repository e condividerlo con gli altri collaboratori.
Il file .git/info/exclude
Nel caso vi sia la necessità di definire dei file da ignorare per esigenze personali e non distribuirli con il repository, è possibile usare le regole viste sopra e inserirle nel file .git/info/exclude Per esempio può essere utile inserire delle regole per escludere i file di supporto generati dal proprio editor di testo o altri strumenti usati in fase di sviluppo.
# esempio file .git/info/exclude
.vscode
Nell’esempio riportato sopra abbiamo inserito all’interno del file .git/info/exclude una regola per cui chiediamo a Git di ignorare per questo progetto file o cartelle generate da Visual Studio Code. Il file .git/info/exclude non verrà inserito all’interno del repository.
Ignorare dei file già tracciati
Abbiamo visto come ignorare dei file. Può capitare di non voler più tenere traccia di un file che era stato precedentemente aggiunto alla Staging area o al repository. Supponiamo per esempio di avere una directory come mostrato in basso. Il file .gitignore è inizialmente vuoto.
[ test_gitignore ] (master) $ tree -aL 1
.
├── .git
├── .gitignore
└── ciao.txt
1 directory, 2 files
Aggiungiamo il file ciao.txt alla Staging Area. E modifichiamo quindi il file .gitignore in modo da escludere il file ciao.txt.
[ test_gitignore ] (master) $ cat ciao.txt
ciao
[ test_gitignore ] (master) $ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
ciao.txt
nothing added to commit but untracked files present (use "git add" to track)
[ test_gitignore ] (master) $ git add ciao.txt
[ test_gitignore ] (master) $ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: ciao.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
[ test_gitignore ] (master) $ echo ciao.txt > .gitignore
[ test_gitignore ] (master) $ echo hello >> ciao.txt
[ test_gitignore ] (master) $ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: ciao.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: ciao.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
Come possiamo notare dall’output dell’ultimo comando, nonostante sia stata inserita una regola nel file .gitignore per escludere il file ciao.txt, Git continua a tenere traccia delle modifiche apportate a quest’ultimo.
[ test_gitignore ] (master) $ git add ciao.txt
[ test_gitignore ] (master) $ git rm --cached ciao.txt
rm 'ciao.txt'
Per risolvere questo problema possiamo rimuovere il file ‘ciao.txt’ dalla Staging area con il comando git rm –cached ciao.txt. Da questo momento in poi, avendo già inserito l’apposita regola per ignorare il file ciao.txt nel file .gitignore, Git ignorerà ogni modifica effettuata sul file stesso. Abbiamo eseguito il comando git add ciao.txt prima di git rm –cached ciao.txt perché avevamo modificato il file ciao.txt e non avevamo aggiunto la nuova versione alla Staging Area. Infatti, quando si usa l’opzione –cached del comando git rm, il file nella Staging Area deve essere uguale al file del Working Tree o a quello presente nel commit a cui punta correntemente HEAD. In caso contrario riceveremo il seguente errore.
error: the following file has staged content different from both the file and the HEAD:
ciao.txt
(use -f to force removal)
Ricapitolando, se vogliamo che Git non tenga traccia di un file già presente nella Staging Area o nel Repository, dobbiamo:
- Aggiungere una regola nel file.gitignore
- Eseguire il comando git rm <nome_del_file> per eliminarlo dalla Staging Area e Working Tree oppure usare l’opzione –cached se si vuole rimuoverlo solo dalla Staging Area (Git non tiene più traccia del file) ma si vuole mantenere il file nella Working Directory.
Inserire nel repository dei file ignorati
Abbiamo visto come sia possibile inserire nel file.gitignore delle regole specifiche che usando il carattere ‘!’ definiscono delle eccezioni a regole più generali.
# ignora tutti i file .gz tranne archivio_speciale.gz
*.gz
!archivio_speciale.gz
Sebbene questa sia la scelta migliore, è possibile in alternativa aggiungere un file ignorato alla Staging Area attraverso l’opzione –force del comando git add. Supponiamo allora che nel file .gitignore sia contenuta una sola regola per cui vengono ignorati tutti i file con estensione .gz.
# file .gitignore
# ignora tutti i file .gz
*.gz
[ test_gitignore ] (master) $ tree -aL 1
.
├── .git
├── .gitignore
├── a.gz
├── b.gz
└── s.gz
1 directory, 4 files
[ test_gitignore ] (master) $ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
# aggiunge il file alla Staging Area con l'opzione --force o -f
[ test_gitignore ] (master) $ git add --force s.gz
[ test_gitignore ] (master) $ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: s.gz
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
[ test_gitignore ] (master) $ git commit -m 'Aggiunge il file s.gz forzatamente'
[master (root-commit) 2075aed] Aggiunge il file s.gz forzatamente
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 s.gz
Ignorare dei file globalmente
È possibile creare un file .gitignore globale che sia valido per tutti i repository locali evitando così di dover ripetere ogni volta la stessa regola per ogni progetto. Per far ciò basta settare la proprietà core.excludesFile globalmente e passare il percorso di un file .gitignore. Vediamo allora un esempio in cui creiamo un file che nominiamo .gitignore_global all’interno della directory dell’utente.
[ ~ ] $ touch .gitignore_global
[ ~ ] $ git config --global core.excludesFile /Users/claudio/.gitignore_global
Da questo momento in poi le regole definite all’interno del file creato saranno valide per ogni repository locale. All’interno del file .gitignore_global possiamo inserire delle regole che consentono di ignorare dei file tipici del sistema operativo su cui stiamo lavorando. Su Github possiamo trovare un repository con degli esempi di file .gitignore contenenti regole diverse a seconda del linguaggio di programmazione e dell’ambiente di lavoro. Vediamo allora un esempio di file .gitignore_global in cui inseriamo alcune regole per il sistama operativo macOS così come viene suggerito nel repository appena mezionato.
# File .gitignore_global
# https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
Conclusioni
In questa lezione abbiamo illustrato i diversi modi messi a disposizione da Git per ignorare dei file. Nella prossima lezione vedremo alcuni utili comandi per visualizzare, elencare e comparare i commit presenti nel repository.