La guida a Git che avresti dovuto leggere prima di fare quel git push --force sul main venerdì alle 18.
Da "git add ." a "so cosa sto facendo (quasi sempre)"
Alla fine Git non è poi così terribile, basta che qualcuno te lo spieghi senza il tono da "senior architect che ha fatto 14 certificazioni".
Non è GitHub. Non è una cartella di backup. Non è magia.
Immagina di scrivere un libro. Git è quel sistema che ti permette di salvare ogni versione di ogni capitolo, tornare indietro a quando il capitolo 3 non faceva schifo, far lavorare 10 persone sullo stesso libro senza che si pestino i piedi, e se qualcuno scrive una cazzata, puoi annullare solo la sua parte senza perdere il resto. È CTRL+Z con i superpoteri.
Git è un sistema di controllo versione distribuito. Ogni modifica al codice viene registrata con chi l'ha fatta, quando, e perché. Come una telecamera di sicurezza per il tuo codice.
Git è il software. Gira sul tuo PC. Gratis, open source, creato da Linus Torvalds nel 2005 perché era incazzato con gli altri tool.
GitHub/GitLab/Bitbucket sono servizi web che ospitano i tuoi repository Git e ci aggiungono feature (PR, issues, CI/CD).
progetto_v1.zip → progetto_v2_finale.zip → progetto_v2_finale_DAVVERO.zip → progetto_ULTIMO_NON_TOCCARE.zip → progetto_OK_QUESTO.zip → 💥
Una cartella. Tutta la storia dentro. Torni a qualsiasi versione con un comando. Lavori in parallelo. Merge automatico. Dormi la notte.
Le 3 zone che devi capire o sei finito
Working Directory = la scrivania dove lavori.
Staging Area = la busta dove metti i fogli da spedire.
Repository = l'archivio dove la busta viene conservata per sempre.
git add = metti nella busta. git commit = sigilla e archivia.
Uno snapshot del progetto. Ha un hash unico (tipo a1b2c3d), un autore, una data e un messaggio.
Una linea di sviluppo parallela. Un puntatore a un commit. Costa zero crearla.
"Dove sei adesso". Punta al branch corrente, che punta all'ultimo commit.
Il server (GitHub, GitLab). "origin" è il nome di default del remote.
Un segnalibro fisso su un commit. Tipo "v1.0.0". Non si muove.
Il cassetto dove nascondi le modifiche temporanee. "Non ho finito ma devo cambiare branch".
5 minuti e non ci pensi più
# macOS (probabilmente ce l'hai già)
git --version # se non c'è, ti chiede di installare Xcode tools
brew install git # oppure con Homebrew
# Linux
sudo apt install git # Debian/Ubuntu
sudo dnf install git # Fedora/RHEL
# Windows
# Scarica da https://git-scm.com e clicca Next 47 volte
# Chi sei? (appare nei commit)
git config --global user.name "Il Tuo Nome"
git config --global user.email "tua@email.com"
# Editor per i messaggi di commit (default: vim 😱)
git config --global core.editor "nano" # o "code --wait" per VS Code
# Branch di default (main invece di master)
git config --global init.defaultBranch "main"
# Colori nel terminale (perché il bianco e nero è triste)
git config --global color.ui auto
# Pull con rebase di default (fidati, è meglio)
git config --global pull.rebase true
# Verifica la config
git config --list
Così non devi scrivere la password ogni volta che fai push. Da svogliato è obbligatorio.
# Genera la chiave
ssh-keygen -t ed25519 -C "tua@email.com"
# Premi Invio 3 volte (password vuota = più svogliato)
# Copia la chiave pubblica
cat ~/.ssh/id_ed25519.pub | pbcopy # macOS (copia negli appunti)
cat ~/.ssh/id_ed25519.pub # Linux (copia a mano)
# Vai su GitHub → Settings → SSH Keys → New SSH Key → Incolla
# Testa
ssh -T git@github.com
Hi username! You've successfully authenticated...
Il 90% del tuo lavoro quotidiano con Git
# Nuovo progetto da zero
mkdir mio-progetto && cd mio-progetto
git init
Initialized empty Git repository in /mio-progetto/.git/
# Oppure clona uno esistente
git clone git@github.com:utente/repo.git
cd repo
Il comando che userai più di qualsiasi altro. Tipo guardare il telefono ma per Git.
git status
On branch main
Changes not staged for commit:
modified: index.html
Untracked files:
nuovo-file.js
# Versione corta (per svogliati)
git status -s
M index.html
?? nuovo-file.js
# Aggiungi file specifici alla staging area
git add index.html
git add src/app.js
# Aggiungi tutto (ATTENZIONE: tutto tutto)
git add .
# Vedi cosa stai per committare
git diff --staged
# Committa con messaggio
git commit -m "aggiungi homepage e logica app"
# Shortcut: add + commit in un colpo (solo file già tracciati!)
git commit -am "fix: correggi bug nel login"
# Log completo
git log
# Log compatto (una riga per commit)
git log --oneline
a1b2c3d fix: correggi bug nel login
e4f5g6h aggiungi homepage e logica app
i7j8k9l initial commit
# Log con grafico dei branch (bellissimo)
git log --oneline --graph --all
# Cosa è cambiato in un commit?
git show a1b2c3d
# Chi ha scritto questa riga? (per dare la colpa)
git blame index.html
# Differenze non ancora staged
git diff
# Differenze staged (pronte per il commit)
git diff --staged
# Differenze tra due commit
git diff a1b2c3d e4f5g6h
# Differenze tra due branch
git diff main..feature/login
# Solo i nomi dei file cambiati
git diff --name-only
.gitignore PRIMA del primo commit. Quello che finisce nella storia di Git ci resta per sempre (quasi).
# Dipendenze
node_modules/
vendor/
venv/
__pycache__/
# File di ambiente (SEGRETI!)
.env
.env.local
*.pem
*.key
# Build
dist/
build/
*.o
*.exe
# IDE
.vscode/
.idea/
*.swp
*.swo
.DS_Store
Thumbs.db
# Log
*.log
logs/
gitignore.io genera .gitignore per qualsiasi linguaggio/framework. Vai su gitignore.io, scrivi "node,python,macos" e copia. Fatto.
Il superpotere di Git. Universi paralleli per il tuo codice.
Un branch è come un universo parallelo del tuo progetto. Crei un branch, fai le tue modifiche, e se tutto va bene lo riunisci al mondo principale. Se va male, lo cancelli e nessuno saprà mai della tua vergogna. Creare un branch costa zero (letteralmente è un file di 41 byte).
# Vedi i branch
git branch # locali
git branch -a # tutti (anche remoti)
# Crea un branch
git branch feature/login
# Spostati su un branch
git checkout feature/login
# Crea E spostati in un colpo (il modo svogliato)
git checkout -b feature/login
# oppure (più moderno)
git switch -c feature/login
# Rinomina il branch corrente
git branch -m nuovo-nome
# Cancella un branch (solo se è stato mergiato)
git branch -d feature/login
# Cancella FORZA (anche se non mergiato - stai attento)
git branch -D feature/login
feature/nome per nuove funzionalità, fix/nome o hotfix/nome per bug, chore/nome per roba noiosa. Il tuo futuro te (e i colleghi) ti ringrazieranno.
La Grande Guerra Santa del mondo Git
Prende due branch e li unisce creando un "merge commit". La storia rimane con tutti i rami visibili. Sicuro, semplice, nessun rischio.
# Sei su main, vuoi portarci feature/login
git checkout main
git merge feature/login
# Se non ci sono conflitti: fatto!
# Se ci sono: vedi sezione Conflitti 💀
Pro: sicuro, non riscrive la storia
Contro: log pieno di merge commit
Prende i tuoi commit e li riapplica sopra un altro branch. Come se avessi iniziato a lavorare dall'ultimo commit di main. Storia lineare e pulita.
# Sei su feature/login, vuoi allinearti a main
git checkout feature/login
git rebase main
# Poi vai su main e fai fast-forward merge
git checkout main
git merge feature/login
Pro: storia lineare e pulita
Contro: riscrive la storia (⚠️ mai su branch condivisi!)
Merge quando unisci un feature branch in main/develop (operazione "pubblica").
Rebase quando aggiorni il tuo branch locale con le ultime modifiche di main (operazione "privata").
In dubbio? Usa merge. Non riscrive niente, non rompe niente.
Dove il tuo codice incontra il mondo esterno
# Vedi i remote configurati
git remote -v
origin git@github.com:utente/repo.git (fetch)
origin git@github.com:utente/repo.git (push)
# Aggiungi un remote
git remote add origin git@github.com:utente/repo.git
# ===== PUSH =====
# Pusha il branch corrente
git push
# Prima push di un nuovo branch (crea il branch remoto)
git push -u origin feature/login
# -u = set upstream, dopo basta "git push"
# ===== PULL =====
# Scarica e unisci le modifiche remote
git pull
# Pull con rebase (storia più pulita)
git pull --rebase
# ===== FETCH =====
# Scarica le modifiche MA non le applica
git fetch
# Utile per "fammi vedere cosa c'è di nuovo" senza toccare niente
# Poi puoi confrontare
git log main..origin/main --oneline
--force-with-lease che almeno controlla se qualcuno ha pushato nel frattempo. Ma meglio ancora: non fare force push su main. Mai.
Il momento in cui tutti odiano Git
Due persone modificano la stessa riga dello stesso file in due branch diversi. Git non sa quale versione tenere e ti chiede di decidere tu. È come quando due coinquilini comprano entrambi il latte: qualcuno deve sceglierne uno.
funzione saluta() {
<<<<<<< HEAD
return "Ciao mondo!"; // la TUA versione (branch corrente)
=======
return "Hello world!"; // la LORO versione (branch che stai mergiando)
>>>>>>> feature/inglese
}
Git non ha rotto niente. Ha solo messo in pausa e ti sta chiedendo aiuto. Respira.
# Vedi quali file hanno conflitti
git status
Unmerged paths:
both modified: index.html
Rimuovi i marcatori (<<<, ===, >>>) e tieni la versione giusta. O combinale. Tu decidi.
// Ho scelto di combinare entrambe
funzione saluta(lingua) {
if (lingua === "it") return "Ciao mondo!";
return "Hello world!";
}
git add index.html
git commit # Git genera già il messaggio di merge
# Se vuoi ABORTIRE il merge (tornare a prima)
git merge --abort
# Come se niente fosse successo. Torna tutto com'era.
Il vero motivo per cui usiamo Git: il CTRL+Z definitivo
# ===== NON HO ANCORA COMMITTATO =====
# Scarta modifiche a un file (IRREVERSIBILE!)
git restore index.html
# Scarta TUTTE le modifiche (IRREVERSIBILE!)
git restore .
# Togli un file dalla staging area (ma tieni le modifiche)
git restore --staged index.html
# ===== HO COMMITTATO MA NON PUSHATO =====
# Modifica l'ultimo commit (messaggio o file)
git commit --amend -m "messaggio corretto"
# Annulla l'ultimo commit MA tieni i file modificati (staged)
git reset --soft HEAD~1
# Annulla l'ultimo commit E togli da staging (file modificati restano)
git reset HEAD~1 # oppure --mixed (default)
# Annulla l'ultimo commit E CANCELLA le modifiche (⚠️ IRREVERSIBILE!)
git reset --hard HEAD~1
# ===== HO GIÀ PUSHATO =====
# Crea un nuovo commit che annulla il precedente (SICURO)
git revert a1b2c3d
git push
# ===== HO PROPRIO ROTTO TUTTO =====
# Il reflog: la scatola nera di Git. Registra TUTTO.
git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
e4f5g6h HEAD@{1}: commit: il commit che pensavi perso
# Torna a qualsiasi punto
git reset --hard e4f5g6h
# Il reflog è il tuo paracadute. Esiste per 30 giorni.
reset --hard, i commit non sono persi subito. git reflog li trova. Hai 30 giorni per recuperare qualsiasi cosa. Git non butta via niente facilmente.
Stai lavorando su qualcosa ma devi cambiare branch urgentemente? Stash salva tutto in un cassetto temporaneo.
# Salva le modifiche nel cassetto
git stash
# Il working directory è pulito, puoi cambiare branch
# Con un nome (per ricordarti cosa c'è dentro)
git stash push -m "fix login a metà"
# Vedi gli stash
git stash list
stash@{0}: On feature: fix login a metà
stash@{1}: WIP on main: roba varia
# Recupera l'ultimo stash
git stash pop # lo applica E lo rimuove dalla lista
git stash apply # lo applica ma lo TIENE nella lista
# Recupera uno stash specifico
git stash pop stash@{1}
# Butta via uno stash
git stash drop stash@{0}
# Butta via TUTTI gli stash
git stash clear
Come lavorare in team senza volersi ammazzare
Il workflow più semplice. Un solo branch principale (main), feature branch per tutto il resto, Pull Request per mergiare. Punto.
# 1. Aggiorna main
git checkout main
git pull
# 2. Crea feature branch
git checkout -b feature/nuova-cosa
# 3. Lavora, committa
git add .
git commit -m "feat: aggiungi la nuova cosa"
# 4. Pusha il branch
git push -u origin feature/nuova-cosa
# 5. Apri una Pull Request su GitHub/GitLab
# 6. Review dei colleghi
# 7. Merge su main
# 8. Cancella il branch
git checkout main
git pull
git branch -d feature/nuova-cosa
Formato standard per i messaggi di commit. Il tuo futuro te (e i colleghi) ti ringrazieranno.
# Struttura: tipo(scope): descrizione
git commit -m "feat: aggiungi pagina di login"
git commit -m "fix: correggi crash su iPhone"
git commit -m "docs: aggiorna README"
git commit -m "refactor: semplifica logica carrello"
git commit -m "chore: aggiorna dipendenze"
git commit -m "test: aggiungi test per il checkout"
git commit -m "style: fix indentazione"
# Con scope (opzionale)
git commit -m "feat(auth): aggiungi login con Google"
git commit -m "fix(api): gestisci timeout su /users"
Perché il terminale è bello ma i conflitti si risolvono meglio con i bottoni
Git integrato in VS Code è già buono. Con GitLens diventa pazzesco: blame inline, storia dei file, confronto branch, tutto a portata di click.
Prezzo: Gratis
Per chi: Chiunque usi VS Code (tutti)
La GUI più bella per Git. Grafo dei branch interattivo, drag&drop per merge/rebase, gestione conflitti visuale. Il Lamborghini delle GUI Git.
Prezzo: Gratis per repo pubblici, a pagamento per privati
Per chi: Chi vuole il meglio visivamente
TUI per Git nel terminale. Come k9s ma per Git. Stage file con spazio, committa con c, pusha con P. Velocissimo.
brew install lazygit # macOS
sudo apt install lazygit # Linux (da PPA)
lazygit # via!
Prezzo: Gratis, open source
Per chi: Chi vive nel terminale ma odia scrivere
Fork (Mac/Win): veloce, pulito, bello. Sourcetree (Atlassian): gratuito, più pesante, integrato con Bitbucket. Entrambi fanno il loro lavoro.
Prezzo: Fork ~$50 una tantum, Sourcetree gratis
Per chi: Chi preferisce app native
# gh: GitHub CLI (gestisci PR, issues, repo dal terminale)
brew install gh
gh pr create --title "feat: nuova cosa" --body "Descrizione"
gh pr list
gh pr merge 42
# delta: diff più bello (syntax highlight, line numbers)
brew install git-delta
# Aggiungilo a .gitconfig e i diff diventano leggibili
# tig: visualizzatore storia interattivo nel terminale
brew install tig
tig
# git-interactive-rebase-tool: rebase interattivo con UI
brew install git-interactive-rebase-tool
Quando tutto va storto e vuoi solo piangere
# 1. Crea il branch dal punto attuale (salva il lavoro)
git branch feature/quello-che-stavo-facendo
# 2. Riporta main a dov'era prima (N = numero di commit da togliere)
git reset --hard HEAD~1 # toglie 1 commit da main
# 3. Spostati sul branch giusto
git checkout feature/quello-che-stavo-facendo
# Fatto! I commit sono sul branch giusto e main è pulito.
PRIMA DI TUTTO: cambia la password / ruota la API key. SUBITO. Anche se la rimuovi dalla storia di Git, qualcuno potrebbe averla già vista.
# 1. CAMBIA LA PASSWORD / KEY (fallo ORA)
# 2. Rimuovi dalla storia con git-filter-repo
pip install git-filter-repo
git filter-repo --path .env --invert-paths
# 3. Force push (necessario dopo filter-repo)
git push --force-with-lease
# 4. Aggiungi .env al .gitignore (fallo subito!)
echo ".env" >> .gitignore
Sei "staccato" da qualsiasi branch. Succede quando fai checkout di un commit specifico o un tag. Se fai commit qui, non sono su nessun branch e li perderai.
# Torna a un branch
git checkout main
# Oppure, se hai fatto commit che vuoi tenere:
git checkout -b salva-il-mio-lavoro
# Ora quei commit sono su un branch e sono salvi
Qualcuno ha pushato su main e tu non hai scaricato le modifiche. Niente panico.
# Se sei su main e vuoi aggiornare
git pull --rebase
# Se sei su un feature branch e main è andato avanti
git fetch
git rebase origin/main
# Opzione 1: Torna all'ultimo commit (perdi modifiche locali!)
git reset --hard HEAD
# Opzione 2: Torna a com'è il remote (NUCLEARE!)
git fetch origin
git reset --hard origin/main
# Opzione 3: Il reflog salva tutto
git reflog
# Trova il punto buono e torna lì
git reset --hard HEAD@{5}
# Opzione 4: L'ultimo ricorso (riclona)
cd ..
mv mio-progetto mio-progetto-rotto
git clone git@github.com:utente/repo.git
# E copia a mano i file che ti servono dal vecchio
# Il reflog è il tuo salvatore (come sempre)
git reflog
# Cerca il commit del branch cancellato
# Ricrea il branch da quel commit
git checkout -b branch-resuscitato a1b2c3d
# Trova i file più grossi nella storia
git rev-list --objects --all | \
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
sort -rnk3 | head -20
# Rimuovi con git-filter-repo
git filter-repo --path quel-video-da-2gb.mp4 --invert-paths
# Per il futuro: usa Git LFS per file grandi
git lfs install
git lfs track "*.mp4" "*.zip" "*.psd"
Stampalo e attaccalo al monitor
| Cosa vuoi fare | Comando |
|---|---|
| Nuovo repo | git init |
| Clona repo | git clone <url> |
| Stato | git status |
| Stage file | git add <file> |
| Stage tutto | git add . |
| Committa | git commit -m "msg" |
| Stage + commit (tracciati) | git commit -am "msg" |
| Pusha | git push |
| Scarica + merge | git pull |
| Solo scarica | git fetch |
| Cosa vuoi fare | Comando |
|---|---|
| Vedi branch | git branch |
| Crea branch | git checkout -b <nome> |
| Cambia branch | git checkout <nome> |
| Merge | git merge <branch> |
| Rebase | git rebase <branch> |
| Cancella branch | git branch -d <nome> |
| Cancella branch remoto | git push origin --delete <nome> |
| Cosa vuoi fare | Comando |
|---|---|
| Scarta modifiche file | git restore <file> |
| Togli da staging | git restore --staged <file> |
| Modifica ultimo commit | git commit --amend |
| Annulla commit (soft) | git reset --soft HEAD~1 |
| Annulla commit (hard) | git reset --hard HEAD~1 |
| Annulla commit pushato | git revert <hash> |
| Salva nel cassetto | git stash |
| Recupera dal cassetto | git stash pop |
| Scatola nera | git reflog |
| Cosa vuoi fare | Comando |
|---|---|
| Log compatto | git log --oneline |
| Log con grafo | git log --oneline --graph --all |
| Diff non staged | git diff |
| Diff staged | git diff --staged |
| Chi ha scritto questa riga | git blame <file> |
| Cerca nel codice | git grep "pattern" |
| Cerca nei commit | git log -S "pattern" |
# Aggiungi nella sezione [alias] del tuo ~/.gitconfig
[alias]
s = status -s
co = checkout
cb = checkout -b
br = branch
ci = commit
ca = commit --amend
lg = log --oneline --graph --all --decorate
last = log -1 --stat
unstage = restore --staged
undo = reset --soft HEAD~1
yolo = push --force-with-lease # almeno non è --force
please = push --force-with-lease # versione educata
wip = !git add . && git commit -m "wip"
nuke = !git reset --hard HEAD && git clean -fd
Committa spesso, pusha frequentemente, scrivi messaggi che abbiano senso.
Branch per tutto. Mai lavorare direttamente su main.
Il reflog è il tuo paracadute. Esiste. Usalo.
In dubbio? git stash e pensa con calma.
Force push su main = licenziamento.