Skip to content

Appunti Git

Published:  at 10:00 AM

Queste sono le note della lezione del corso su Git. Nelle prossime lezioni il progetto della pizzeria smette di essere una manciata di file e diventa un’applicazione vera, generata da una CLI, con decine di file e dipendenze. Da lì in poi lavorare senza un sistema di controllo versione smette di essere una scelta discutibile e diventa autolesionismo, quindi la lezione su Git va fatta adesso.

Tutti, prima di Git, siamo passati dalla stessa strada: la cartella progetto-finale, poi progetto-finale-DEFINITIVO, poi progetto-finale-DEFINITIVO-v2-questa-buona. Git risolve quel problema lì, e parecchi altri che non sapevate ancora di avere: una storia completa del progetto, la possibilità di tornare indietro, di lavorare in parallelo su più cose e di collaborare senza passarsi gli zip.

Sistemiamo subito anche l’equivoco più diffuso, perché lo sento a ogni corso: Git e GitHub sono due cose diverse. Git è il software di controllo versione, gira sul vostro computer e funziona benissimo anche senza internet. GitHub è un sito che ospita repository Git e ci costruisce sopra strumenti di collaborazione. Si può usare Git senza GitHub; il contrario no.

Configurazione iniziale

Git firma ogni salvataggio con un nome e una email, quindi la prima cosa da fare, una volta sola, è dirgli chi siete:

git config --global user.name "Paolino Paperino"
git config --global user.email "paolino@example.com"

Il primo repository

Un repository è un progetto sotto controllo versione. Per crearlo si entra nella cartella del progetto e si inizializza:

cd awesome-pizza
git init

Da fuori non è cambiato niente, e in effetti è cambiata una cosa sola: è comparsa una cartella nascosta .git, che è il database dove Git terrà tutta la storia del progetto. Il resto della cartella, i vostri file, è quella che Git chiama working directory. Non toccate mai niente dentro .git, e soprattutto non cancellatela: cancellarla significa cancellare la storia.

I tre stati e lo staging

Qui sta il modello mentale di Git, e vale la pena capirlo subito perché spiega il novanta per cento dei comandi. Una modifica attraversa tre zone:

  1. la working directory, dove modificate i file
  2. la staging area, dove preparate ciò che entrerà nel prossimo salvataggio
  3. il repository, dove i salvataggi diventano permanenti

I salvataggi si chiamano commit, e ognuno è una fotografia del progetto in un certo momento. Il giro completo:

git status
git add index.html
git commit -m "Aggiungi la pagina del menu"

git status vi dice in ogni momento cosa è modificato, cosa è in staging e cosa non è ancora tracciato. Prendete l’abitudine di lanciarlo in continuazione: è la bussola, non costa niente, e non ho mai visto nessuno pentirsi di un git status di troppo.

Una domanda legittima a questo punto: perché due passaggi? Non basterebbe un comando che salva tutto? Lo staging esiste perché un commit dovrebbe contenere una modifica logica, una sola. Se avete sistemato il form degli ordini e nel frattempo corretto un refuso nel footer, lo staging vi permette di farne due commit separati, scegliendo file per file cosa entra. La scorciatoia git add . mette in staging tutto, e va benissimo, finché sapete cosa state aggiungendo.

La storia

Ogni commit finisce nella storia del progetto, che si consulta con:

git log --oneline

L’output è la cronaca del progetto, un commit per riga:

f3a91c2 Aggiungi il form per gli ordini
8b27d10 Aggiungi la pagina del menu
a1c4e89 Primo commit

DON’T: messaggi di commit inutili

Prima o poi aprirete la storia di un progetto e troverete fix, modifiche, asd, ora funziona. Il messaggio di commit è documentazione: dice al collega del futuro, che statisticamente siete voi tra sei mesi, perché quella modifica esiste. Non serve un poema, serve una riga onesta che descriva la modifica: “Aggiungi la validazione al form degli ordini” batte “fix form” tutti i giorni.

.gitignore

Non tutto quello che sta nella cartella del progetto merita di entrare nella storia. L’esempio principe lo incontrerete alla prossima lezione: la cartella node_modules, che contiene le dipendenze scaricate, pesa centinaia di megabyte e si rigenera con un npm install. Versionarla è inutile e doloroso.

Il file .gitignore, nella radice del repository, elenca ciò che Git deve fingere di non vedere:

node_modules/
dist/
.DS_Store

Le CLI che useremo nelle prossime lezioni generano progetti con un .gitignore già pronto, ed è uno dei motivi per cui questa lezione viene prima di quelle.

Branch

Finora la storia è una linea retta. I branch permettono di farla ramificare: una linea di sviluppo parallela dove lavorare a una funzionalità senza toccare quella principale, che per convenzione si chiama main.

La pizzeria vuole sperimentare il menu estivo senza rompere il sito in produzione:

git switch -c menu-estivo

switch -c crea il branch e ci si sposta sopra. Nei tutorial più vecchi troverete git checkout -b, che fa la stessa cosa: switch è il comando moderno, nato apposta perché checkout faceva troppe cose diverse. Da qui in poi i commit finiscono sul branch menu-estivo, e main resta esattamente dov’era.

Quando il menu estivo è pronto, si torna su main e si fondono le storie:

git switch main
git merge menu-estivo

I conflitti, senza panico

Se le due linee di sviluppo hanno modificato le stesse righe degli stessi file, Git non decide al posto vostro: si ferma e ve lo chiede. Il file incriminato si presenta così:

<<<<<<< HEAD
<h2>Le nostre pizze</h2>
=======
<h2>Il menu dell'estate</h2>
>>>>>>> menu-estivo

Tra <<<<<<< e ======= c’è la versione del branch su cui siete, tra ======= e >>>>>>> quella del branch che state fondendo. Risolvere un conflitto significa decidere come deve essere il risultato: si modifica il file lasciando il contenuto giusto, si tolgono i marcatori, poi git add e git commit. Tutto qui. Il primo conflitto fa sempre una certa impressione, dal secondo in poi è ordinaria amministrazione.

Remoti e GitHub

Tutto quello visto finora vive sul vostro computer. Un remote è una copia del repository ospitata altrove, tipicamente su GitHub, che fa da punto di sincronizzazione e, non guasta, da backup.

Per portarsi sul computer un progetto esistente:

git clone https://github.com/awesomepizza/sito.git

Per mandare i propri commit al remote e per scaricare quelli degli altri:

git push
git pull

Il flusso quotidiano di chi lavora in squadra è un giro regolare: git pull per allinearsi, si lavora, git add, git commit, git push. Sul mondo che GitHub costruisce attorno a questo flusso, le pull request, le review, le issue, ci sarebbe da fare una lezione a parte; per ora vi basta il giro.

Salvagente

Chiudo con i due comandi da conoscere per i piccoli incidenti di tutti i giorni.

Avete modificato un file e ve ne siete pentiti, volete tornare all’ultimo commit:

git restore index.html

Attenzione: le modifiche scartate non si recuperano, quindi usatelo quando siete sicuri.

Avete appena fatto un commit e il messaggio è sbagliato:

git commit --amend -m "Il messaggio giusto"

Git ha una famiglia intera di comandi per riscrivere la storia, e prima o poi sentirete nominare rebase con un misto di rispetto e paura. Tutto a suo tempo: con questi due, git status e un po’ di disciplina sui commit andate avanti per mesi.

Per approfondire