Git oltre le basi: workflow che fanno risparmiare ore ogni settimana
Interactive rebase, cherry-pick, bisect, worktree, recupero con reflog e le strategie di branching che funzionano davvero. Comandi Git che uso ogni giorno e che la maggior parte degli sviluppatori non sa che esistono.
La maggior parte degli sviluppatori impara cinque comandi Git e si ferma lì. add, commit, push, pull, merge. Forse checkout e branch se si sentono avventurosi. Questo ti porta avanti per il primo anno. Poi il tuo branch ha 47 commit con messaggi come "fix" e "wip" e "please work", fai accidentalmente reset di qualcosa che non dovevi, e passi 40 minuti su Stack Overflow cercando di annullare un merge andato male.
Uso Git da anni. Non casualmente — intensamente. Più branch, più repository, più collaboratori, tutto il giorno, ogni giorno. Quello che segue sono i comandi e i workflow che uso effettivamente. Non quelli che fanno bella figura in un tutorial. Quelli che mi fanno risparmiare tempo reale, ogni singola settimana.
Interactive Rebase: pulisci il tuo pasticcio prima che qualcuno lo veda#
Il tuo branch ha dodici commit. La metà sono "fix typo." Uno dice "undo previous commit." Un altro dice "actually fix it this time." Stai per aprire una PR. Nessuno ha bisogno di vedere quella storia.
L'interactive rebase è il modo in cui riscrivi la storia sul tuo branch prima di condividerla. Ti permette di fare squash dei commit insieme, riscrivere i messaggi, riordinarli o eliminarli completamente.
Il comando base#
git rebase -i HEAD~5Questo apre un editor che mostra i tuoi ultimi 5 commit, dal più vecchio al più recente:
pick a1b2c3d Add user authentication endpoint
pick d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
pick l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsOgni riga inizia con un comando. Cambia pick in uno di questi:
squash(os) — Unisci questo commit a quello sopra, combinando i messaggifixup(of) — Come squash, ma scarta il messaggio di questo commitreword(or) — Mantieni il commit ma cambia il suo messaggiodrop(od) — Elimina completamente questo commitedit(oe) — Metti in pausa il rebase a questo commit per modificarlo
Una vera sessione di pulizia#
Ecco cosa faccio realmente. Quella storia disordinata diventa:
pick a1b2c3d Add user authentication endpoint
fixup d4e5f6g Fix typo in auth middleware
pick h7i8j9k Add rate limiting
fixup l0m1n2o Fix rate limit bug
pick p3q4r5s Update auth testsSalva e chiudi. Ora hai tre commit puliti invece di cinque. Il fix del typo viene incorporato nel commit dell'autenticazione. Il fix del bug del rate limit viene incorporato nel commit del rate limit. Il tuo revisore della PR vede una progressione pulita e logica.
Riordinamento dei commit#
Puoi letteralmente riordinare le righe. Se il commit dei test dovrebbe venire prima del commit del rate limiting, sposta semplicemente la riga:
pick a1b2c3d Add user authentication endpoint
pick p3q4r5s Update auth tests
pick h7i8j9k Add rate limitingGit riprodurrà i tuoi commit in questo nuovo ordine. Se ci sono conflitti, si metterà in pausa e ti lascerà risolverli.
La scorciatoia autosquash#
Se sai che un commit è un fix per uno precedente, marcalo al momento del commit:
git commit --fixup=a1b2c3dQuesto crea un commit con il messaggio fixup! Add user authentication endpoint. Poi quando fai rebase:
git rebase -i --autosquash HEAD~5Git riordina automaticamente i commit fixup subito sotto i loro target e li marca come fixup. Salvi e chiudi. Nessuna modifica manuale.
Lo uso costantemente. È il modo più veloce per iterare su un branch mantenendo pulita la storia finale.
La regola d'oro#
Non fare mai rebase di commit che sono stati pushati su un branch condiviso. Se altre persone hanno basato del lavoro su quei commit, riscrivere la storia causerà problemi reali. Fai rebase dei tuoi feature branch prima di mergiare. Non fare mai rebase di main.
Se hai già pushato il tuo feature branch e devi fare rebase:
git push --force-with-leaseIl flag --force-with-lease è più sicuro di --force. Rifiuta di pushare se qualcun altro ha pushato sullo stesso branch dal tuo ultimo fetch. Non previene tutti i problemi, ma cattura quello più comune.
Cherry-Pick: trasferimenti chirurgici di commit#
Cherry-pick prende un commit specifico da un branch e lo applica a un altro. Non un merge. Non un rebase. Solo un commit, applicato in modo pulito.
Quando lo uso realmente#
Lo scenario più comune: ho fixato un bug sul mio feature branch, ma il fix deve andare su main o su un release branch subito. Non voglio mergiare l'intero feature branch. Voglio solo quel fix.
# Trova l'hash del commit del fix
git log --oneline feature/user-auth
# a1b2c3d Fix null pointer in session validation
# Passa a main e fai cherry-pick
git checkout main
git cherry-pick a1b2c3dFatto. Il fix è su main come un nuovo commit con le stesse modifiche.
Cherry-pick senza committare#
A volte vuoi applicare le modifiche ma non committarle ancora. Forse vuoi combinare diversi cherry-pick in un unico commit, o modificare leggermente le modifiche:
git cherry-pick --no-commit a1b2c3dLe modifiche sono in staging ma non committate. Puoi modificarle, aggiungere altre modifiche, poi committare quando sei pronto.
Cherry-pick a intervallo#
Hai bisogno di più commit consecutivi? Usa la sintassi a intervallo:
git cherry-pick a1b2c3d..f6g7h8iQuesto fa cherry-pick di tutto dopo a1b2c3d fino a e incluso f6g7h8i. Nota che a1b2c3d stesso è escluso. Se vuoi includerlo:
git cherry-pick a1b2c3d^..f6g7h8iGestione dei conflitti#
I conflitti del cherry-pick funzionano come i conflitti del merge. Quando ne succede uno:
# Git ti dirà che c'è un conflitto
# Fixa i file in conflitto, poi:
git add .
git cherry-pick --continueOppure se cambi idea:
git cherry-pick --abortUna cosa da tenere presente: i commit cherry-picked creano nuovi hash di commit. Se più tardi fai merge del branch originale, Git è di solito abbastanza intelligente da gestire la duplicazione. Ma se fai cherry-pick in modo aggressivo tra branch che alla fine verranno mergiati, potresti vedere conflitti inaspettati. Usalo chirurgicamente, non come strategia di merge.
Git Bisect: ricerca binaria per bug#
Qualcosa si è rotto. Sai che funzionava due settimane fa. Ci sono stati 200 commit da allora. Quale l'ha rotto?
Potresti controllare ogni commit manualmente. Oppure potresti usare git bisect, che usa la ricerca binaria per trovare l'esatto commit che ha rotto tutto in log2(n) passi. Per 200 commit, sono circa 7-8 controlli invece di 200.
Il modo manuale#
# Inizia il bisect
git bisect start
# Marca il commit corrente come cattivo (il bug esiste qui)
git bisect bad
# Marca un commit noto come buono (il bug non esisteva qui)
git bisect good v2.1.0Git fa checkout di un commit a metà strada tra buono e cattivo. Testalo. Poi:
# Se il bug esiste in questo commit:
git bisect bad
# Se il bug non esiste in questo commit:
git bisect goodGit dimezza l'intervallo ogni volta. Dopo 7-8 passi, ti dice:
a1b2c3d4e5f6g7h is the first bad commit
commit a1b2c3d4e5f6g7h
Author: Some Developer <dev@example.com>
Date: Tue Feb 18 14:23:01 2026 +0300
Refactor session handling to use async middlewareOra sai esattamente quale commit ha introdotto il bug. Quando hai finito:
git bisect resetQuesto ti riporta da dove sei partito.
Il modo automatizzato (questo è il vero potere)#
Se hai un test che può rilevare il bug, puoi automatizzare l'intero processo:
git bisect start
git bisect bad HEAD
git bisect good v2.1.0
git bisect run npm test -- --grep "session validation"Git farà automaticamente checkout dei commit, eseguirà il test e li segnerà come buoni o cattivi in base al codice di uscita. Zero significa buono, diverso da zero significa cattivo. Allontanati, torna, e ti dice il commit esatto.
Puoi usare qualsiasi script:
git bisect run ./test-regression.shDove test-regression.sh è:
#!/bin/bash
npm run build 2>/dev/null || exit 125 # 125 significa "salta questo commit"
npm test -- --grep "session" || exit 1 # 1 significa "cattivo"
exit 0 # 0 significa "buono"Il codice di uscita 125 è speciale — dice a bisect di saltare quel commit (utile se un commit non compila). Questa è una di quelle funzionalità che sembra di nicchia finché non ne hai bisogno, e poi ti risparmia un intero pomeriggio.
Una vera sessione di bisect#
Ecco come appare in pratica:
$ git bisect start
$ git bisect bad HEAD
$ git bisect good abc1234
Bisecting: 97 revisions left to test after this (roughly 7 steps)
[def5678...] Commit message here
$ npm test -- --grep "login flow"
# I test falliscono
$ git bisect bad
Bisecting: 48 revisions left to test after this (roughly 6 steps)
[ghi9012...] Another commit message
$ npm test -- --grep "login flow"
# I test passano
$ git bisect good
Bisecting: 24 revisions left to test after this (roughly 5 steps)
...
# Dopo ~7 iterazioni:
abc1234def5678ghi is the first bad commitSette passi per trovare un ago in un pagliaio di 200 commit.
Git Worktree: più branch, zero stash#
Questa è la funzionalità Git più sottoutilizzata. Sono convinto che la maggior parte degli sviluppatori non sappia nemmeno che esiste.
Il problema: sei immerso in un feature branch. I file sono cambiati ovunque. Poi qualcuno dice "puoi dare un'occhiata veloce a questo bug in produzione?" Hai tre opzioni:
- Stasha tutto, cambia branch, fixa, ritorna, pop dello stash. Sperando che niente vada storto.
- Committa il tuo lavoro a metà con un messaggio "wip". Brutto ma funzionale.
- Clona il repository di nuovo in una directory diversa.
Oppure opzione 4: worktree.
Cos'è un worktree#
Un worktree è una seconda (o terza, o quarta) directory di lavoro collegata allo stesso repository. Ogni worktree ha il proprio branch checkoutato, i propri file di lavoro, il proprio indice. Ma condividono gli stessi dati .git, quindi non stai duplicando l'intero repository.
Aggiungere un worktree#
# Sei su feature/user-auth, immerso nel lavoro
# Devi fixare un bug su main:
git worktree add ../hotfix-session mainQuesto crea una nuova directory ../hotfix-session con main checkoutato. La tua directory corrente resta esattamente com'era. Niente stashato, niente committato, niente interrotto.
cd ../hotfix-session
# Fixa il bug
git add .
git commit -m "Fix null pointer in session validation"
git push origin main
cd ../my-project
# Continua a lavorare sulla tua feature come se nulla fosse successoCreare un nuovo branch in un worktree#
git worktree add ../hotfix-nav -b hotfix/nav-crash mainQuesto crea il worktree E crea un nuovo branch hotfix/nav-crash basato su main.
Gestione dei worktree#
# Elenca tutti i worktree
git worktree list
# /home/dev/my-project abc1234 [feature/user-auth]
# /home/dev/hotfix-session def5678 [main]
# Rimuovi un worktree quando hai finito
git worktree remove ../hotfix-session
# Se la directory è già stata eliminata:
git worktree prunePerché è meglio dello stash#
Lo stash va bene per cambi di contesto rapidi. Ma i worktree sono migliori per qualsiasi cosa che duri più di cinque minuti:
- Il tuo IDE resta aperto sul tuo feature branch. Nessuna reindicizzazione, nessuna perdita della posizione di scroll.
- Puoi eseguire test in entrambe le directory simultaneamente.
- Nessun rischio di conflitti dello stash o di dimenticare cosa hai stashato.
- Puoi avere un dev server in esecuzione in un worktree e una build pulita in un altro.
Tipicamente tengo due o tre worktree attivi: il mio feature branch principale, un worktree su main per controlli veloci, e a volte un worktree di review dove faccio checkout della PR di qualcun altro.
L'unica limitazione#
Non puoi avere lo stesso branch checkoutato in due worktree. È per design — ti impedisce di fare modifiche in conflitto allo stesso branch in due posti. Se ci provi, Git rifiuterà.
Reflog: il pulsante annulla per tutto#
Hai fatto un hard reset e perso dei commit. Hai cancellato un branch. Hai fatto rebase e qualcosa è andato orribilmente male. Pensi che il tuo lavoro sia andato.
Non lo è. Git non cancella quasi mai niente davvero. Il reflog è la tua rete di sicurezza.
Cos'è il reflog#
Ogni volta che HEAD si muove — ogni commit, checkout, rebase, reset, merge — Git lo registra nel reflog. È un log di dove è stato il tuo HEAD, in ordine.
git reflogOutput:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: Add payment processing
b7a8c9d HEAD@{2}: commit: Update user dashboard
e0f1g2h HEAD@{3}: commit: Fix auth token refresh
i3j4k5l HEAD@{4}: checkout: moving from feature/payments to mainOgni voce ha un indice (HEAD@{0}, HEAD@{1}, ecc.) e una descrizione di cosa è successo.
Recupero dopo un hard reset#
Hai accidentalmente eseguito git reset --hard HEAD~3 e perso tre commit. Sono proprio lì nel reflog:
# Guarda cosa hai perso
git reflog
# Il commit prima del reset è HEAD@{1}
git reset --hard f4e5d6cTutti e tre i commit sono tornati. Crisi sventata.
Recupero di un branch cancellato#
Hai cancellato un branch che aveva lavoro non mergiato:
git branch -D feature/experimental
# Oh no, c'erano due settimane di lavoroI commit esistono ancora. Trovali:
git reflog | grep "feature/experimental"
# Oppure cerca nel reflog l'ultimo commit su quel branch
# Trovato. Ricrea il branch a quel commit:
git branch feature/experimental a1b2c3dIl branch è tornato, con tutti i suoi commit.
Recupero dopo un rebase andato male#
Hai fatto rebase e tutto è andato storto. Conflitti ovunque, commit sbagliati, caos:
# Il reflog mostra dove eri prima del rebase
git reflog
# a1b2c3d HEAD@{0}: rebase (finish): ...
# ...
# f4e5d6c HEAD@{5}: rebase (start): checkout main
# b7a8c9d HEAD@{6}: commit: Your last good commit
# Torna a prima del rebase
git reset --hard b7a8c9dSei tornato esattamente dove eri prima che il rebase iniziasse. Come se non fosse mai successo.
La rete di sicurezza dei 30 giorni#
Di default, Git mantiene le voci del reflog per 30 giorni (90 giorni per i commit raggiungibili). Dopo, possono essere eliminati dal garbage collector. Quindi hai un mese per realizzare di aver fatto un errore. In pratica, è più che sufficiente.
Puoi controllare la scadenza:
git config gc.reflogExpire
# default: 90.days.ago (per i raggiungibili)
git config gc.reflogExpireUnreachable
# default: 30.days.ago (per gli irraggiungibili)Se sei paranoico, aumentalo:
git config --global gc.reflogExpireUnreachable "180.days.ago"Una regola personale#
Prima di qualsiasi operazione distruttiva — hard reset, force push, cancellazione di branch — eseguo prima git log --oneline -10. Prendo nota mentalmente dell'HEAD corrente. Ci vogliono due secondi e mi ha salvato più di una volta da un panico di cui non avevo bisogno.
Stash correttamente: non è solo git stash#
La maggior parte delle persone usa lo stash così:
git stash
# fai qualcosa
git stash popFunziona, ma è l'equivalente di buttare tutto in una scatola etichettata "roba." Quando hai tre stash, non hai idea di quale sia quale.
Dai un nome ai tuoi stash#
git stash push -m "WIP: user auth form validation"Ora quando elenchi gli stash:
git stash list
# stash@{0}: On feature/auth: WIP: user auth form validation
# stash@{1}: On main: Quick fix attempt for nav bug
# stash@{2}: On feature/payments: Experiment with Stripe webhooksPuoi vedere esattamente cosa contiene ogni stash.
Includi i file non tracciati#
Di default, git stash stasha solo i file tracciati. I nuovi file che non hai ancora aggiunto vengono lasciati indietro:
# Stasha tutto, compresi i nuovi file
git stash push --include-untracked -m "WIP: new auth components"
# O includi anche i file ignorati (raramente necessario)
git stash push --all -m "Full workspace snapshot"Uso --include-untracked quasi ogni volta. Lasciare nuovi file quando cambi branch causa confusione.
Stash parziale#
Questo è quello che la maggior parte delle persone non conosce. Puoi stashare file specifici:
# Stasha solo file specifici
git stash push -m "Just the auth changes" src/auth/ src/middleware.tsOppure usa la modalità patch per stashare hunk specifici all'interno dei file:
git stash push --patch -m "Partial: only the validation logic"Git passerà in rassegna ogni modifica interattivamente e chiederà se vuoi stasharla. y per sì, n per no, s per dividere l'hunk in pezzi più piccoli.
Apply vs pop#
# Pop: applica e rimuovi dalla lista degli stash
git stash pop stash@{2}
# Apply: applica ma mantieni nella lista degli stash
git stash apply stash@{2}Uso apply quando non sono sicuro che lo stash si applicherà in modo pulito. Se c'è un conflitto, lo stash viene preservato. Con pop, se c'è un conflitto, lo stash resta nella lista comunque (molti non lo sanno), ma preferisco l'intento esplicito di apply.
Visualizzazione del contenuto dello stash#
# Vedi quali file sono cambiati in uno stash
git stash show stash@{0}
# Vedi il diff completo
git stash show -p stash@{0}Creare un branch da uno stash#
Se il tuo stash è cresciuto in qualcosa di più sostanziale:
git stash branch feature/auth-validation stash@{0}Questo crea un nuovo branch dal commit dove originariamente hai stashato, applica lo stash e lo elimina. Pulito.
Strategie di branching: quale funziona davvero#
Ci sono tre strategie di branching mainstream. Ciascuna ha un contesto in cui eccelle e contesti in cui causa problemi.
Gitflow#
Il classico. main, develop, feature/*, release/*, hotfix/*. Creato da Vincent Driessen nel 2010.
# Feature branch
git checkout -b feature/user-auth develop
# ... lavora ...
git checkout develop
git merge --no-ff feature/user-auth
# Release branch
git checkout -b release/2.1.0 develop
# ... fix finali ...
git checkout main
git merge --no-ff release/2.1.0
git tag -a v2.1.0 -m "Release 2.1.0"
git checkout develop
git merge --no-ff release/2.1.0
# Hotfix
git checkout -b hotfix/session-fix main
# ... fix ...
git checkout main
git merge --no-ff hotfix/session-fix
git checkout develop
git merge --no-ff hotfix/session-fixQuando funziona: App mobile, software desktop, qualsiasi cosa con release versionati e nominati e versioni multiple supportate simultaneamente. Se rilasci la v2.1 e la v3.0 e devi patchare entrambe, Gitflow gestisce questo.
Quando non funziona: Applicazioni web con deployment continuo. Se fai deploy in produzione 5 volte al giorno, la cerimonia dei release branch e dei develop branch è puro overhead. La maggior parte dei team web che adotta Gitflow finisce con un branch develop che è perennemente rotto e release branch che nessuno capisce.
GitHub Flow#
Semplice. Hai main. Crei feature branch. Apri PR. Mergi su main. Fai deploy di main.
git checkout -b feature/user-auth main
# ... lavora ...
git push origin feature/user-auth
# Apri PR, ricevi la review, mergia
# main è sempre deployabileQuando funziona: Team piccoli-medi che rilasciano app web. Deployment continuo. Se main va sempre in produzione, questo è tutto ciò di cui hai bisogno. È ciò che usa questo sito.
Quando non funziona: Quando devi mantenere versioni multiple di release, o quando hai un lungo ciclo QA prima del deployment. GitHub Flow assume che main vada in produzione velocemente.
Trunk-Based Development#
Tutti committano su main (il "trunk") direttamente o tramite branch molto short-lived (meno di un giorno). Nessun feature branch di lunga durata.
# Branch short-lived (mergiato lo stesso giorno)
git checkout -b fix/auth-token main
# ... modifica piccola e mirata ...
git push origin fix/auth-token
# PR revisionata e mergiata in ore, non giorniQuando funziona: Team ad alte prestazioni con buona CI/CD, suite di test complete e feature flag. Google, Meta e la maggior parte delle grandi aziende tech usano il trunk-based development. Forza modifiche piccole e incrementali ed elimina l'inferno del merge.
Quando non funziona: Team senza buona copertura dei test o CI. Se mergiare sul trunk significa deployare codice non testato, romperai la produzione costantemente. Hai anche bisogno di feature flag per qualsiasi cosa che richieda più di un giorno da costruire:
# Feature flag nel codice
if (featureFlags.isEnabled('new-checkout-flow')) {
renderNewCheckout();
} else {
renderLegacyCheckout();
}La mia raccomandazione#
Per la maggior parte dei team di web development: inizia con GitHub Flow. È semplice, funziona, e non richiede tooling oltre a quello che GitHub/GitLab fornisce già.
Se il tuo team cresce oltre 15-20 ingegneri e fai deploy più volte al giorno, guarda al trunk-based development con feature flag. L'investimento nell'infrastruttura dei feature flag si ripaga da solo in conflitti di merge ridotti e iterazione più veloce.
Se rilasci software versionato (app mobile, strumenti CLI, librerie): Gitflow o una versione semplificata. Hai davvero bisogno di quei release branch.
Non scegliere una strategia perché un post di blog ha detto che è la migliore. Scegli quella che corrisponde a come rilasci effettivamente.
Git Hook: automatizza le cose che continui a dimenticare#
I Git hook sono script che si eseguono automaticamente in momenti specifici del workflow Git. Sono locali sulla tua macchina (non vengono pushati al remote), il che significa che hai bisogno di un modo per condividerli con il team.
Gli hook che contano davvero#
pre-commit — Si esegue prima di ogni commit. Usalo per linting e formattazione:
#!/bin/bash
# .git/hooks/pre-commit
# Esegui ESLint solo sui file in staging
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
if [ -n "$STAGED_FILES" ]; then
echo "Esecuzione ESLint sui file in staging..."
npx eslint $STAGED_FILES --quiet
if [ $? -ne 0 ]; then
echo "ESLint fallito. Correggi gli errori prima di committare."
exit 1
fi
fi
# Esegui Prettier sui file in staging
if [ -n "$STAGED_FILES" ]; then
echo "Esecuzione Prettier..."
npx prettier --check $STAGED_FILES
if [ $? -ne 0 ]; then
echo "Check Prettier fallito. Esegui prima 'npx prettier --write'."
exit 1
fi
ficommit-msg — Valida il formato del messaggio di commit. Perfetto per imporre i conventional commit:
#!/bin/bash
# .git/hooks/commit-msg
COMMIT_MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,72}"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "Formato messaggio di commit non valido."
echo "Atteso: type(scope): description"
echo "Esempio: feat(auth): add session refresh endpoint"
echo ""
echo "Tipi validi: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
exit 1
fipre-push — Si esegue prima del push. Usalo per i test:
#!/bin/bash
# .git/hooks/pre-push
echo "Esecuzione test prima del push..."
npm test
if [ $? -ne 0 ]; then
echo "Test falliti. Push annullato."
exit 1
fiHusky vs hook nativi#
Gli hook nativi vivono in .git/hooks/. Il problema: la directory .git non è tracciata da Git, quindi non puoi condividere gli hook attraverso il repository. Tutti devono configurarli manualmente.
Husky risolve questo. Memorizza le configurazioni degli hook nel repository e configura .git/hooks automaticamente su npm install:
npx husky initQuesto crea una directory .husky/. Aggiungi gli hook come file:
# .husky/pre-commit
npx lint-stagedCombinato con lint-staged, ottieni controlli pre-commit veloci e mirati:
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.css": ["prettier --write"],
"*.json": ["prettier --write"]
}
}Questo esegue ESLint e Prettier solo sui file che stai effettivamente committando. Non sull'intero codebase. Veloce.
Quando saltare gli hook#
A volte devi committare senza che gli hook si eseguano. Hotfix di emergenza, commit work-in-progress sul tuo branch:
git commit --no-verify -m "WIP: debugging production issue"Usa questo con parsimonia. Se ti ritrovi a saltare gli hook regolarmente, i tuoi hook sono probabilmente troppo lenti o troppo rigidi.
Alias che fanno risparmiare tempo#
Il mio .gitconfig si è evoluto nel corso degli anni. Questi sono gli alias che sono sopravvissuti — quelli che uso davvero ogni giorno, non quelli che ho aggiunto perché sembravano intelligenti.
Log leggibile#
Il git log predefinito è verboso. Questo ti dà una vista pulita, colorata e basata su grafo:
git config --global alias.lg "log --oneline --graph --all --decorate"Utilizzo:
git lg
# * a1b2c3d (HEAD -> main) Fix session validation
# | * d4e5f6g (feature/payments) Add Stripe integration
# | * h7i8j9k Update payment models
# |/
# * l0m1n2o Merge PR #42
# * p3q4r5s Add user dashboardLo eseguo 20 volte al giorno. È il modo più veloce per capire lo stato del tuo repository.
Annulla l'ultimo commit#
Mantieni le modifiche, annulla solo il commit:
git config --global alias.undo "reset HEAD~1 --mixed"Utilizzo:
git undo
# Il commit è sparito, ma tutte le modifiche sono ancora nella tua directory di lavoroLo uso quando committo troppo presto, dimentico un file, o voglio ristrutturare le modifiche.
Rimuovi tutto dallo staging#
git config --global alias.unstage "reset HEAD --"Utilizzo:
git unstage src/auth/session.ts
# Il file è rimosso dallo staging ma le modifiche sono preservateElenca tutti gli alias#
Perché dimenticherai cosa hai configurato:
git config --global alias.aliases "config --get-regexp ^alias\\."Altri alias che uso#
# Mostra cosa stai per committare
git config --global alias.staged "diff --staged"
# Stato breve
git config --global alias.st "status -sb"
# Amend senza cambiare il messaggio
git config --global alias.amend "commit --amend --no-edit"
# Mostra l'ultimo commit
git config --global alias.last "log -1 HEAD --stat"
# Pull con rebase invece di merge
git config --global alias.up "pull --rebase --autostash"
# Elimina i branch che sono stati mergiati a main
git config --global alias.cleanup "!git branch --merged main | grep -v '^[ *]*main$' | xargs git branch -d"L'alias up è particolarmente buono. pull --rebase mantiene la tua storia lineare invece di creare commit di merge per ogni pull. --autostash stasha e ripristina automaticamente le tue modifiche se hai file sporchi. È ciò che il pull avrebbe dovuto essere di default.
L'alias cleanup elimina i branch locali che sono stati mergiati a main. Con il tempo, accumuli dozzine di branch stantii. Esegui questo settimanalmente.
I comandi che uso quotidianamente#
Questi non sono alias o funzionalità avanzate. Sono solo comandi che eseguo costantemente e che molti sviluppatori non sembrano conoscere.
Il comando log definitivo#
git log --oneline --graph --allQuesto mostra ogni branch, ogni merge, l'intera topologia del tuo repository in una vista compatta. È la prima cosa che eseguo quando faccio pull delle modifiche. Risponde a "cosa sta succedendo in questo repository adesso?"
Diff delle modifiche in staging#
git diff --stagedQuesto mostra cosa sta per essere committato. Non cosa è cambiato nella tua directory di lavoro — cosa è effettivamente in staging. Lo eseguo sempre prima di committare. Sempre. Cattura inclusioni accidentali, statement di debug, console.log che non dovrebbero essere lì.
# Vedi le modifiche non in staging
git diff
# Vedi le modifiche in staging
git diff --staged
# Vedi entrambe
git diff HEADMostra un commit specifico#
git show a1b2c3dMostra il diff completo di un singolo commit. Utile quando rivedi la storia, per capire cosa ha effettivamente cambiato un commit.
# Mostra solo i file cambiati
git show --stat a1b2c3d
# Mostra un file specifico a un commit specifico
git show a1b2c3d:src/auth/session.tsQuest'ultimo è incredibilmente utile. Puoi visualizzare qualsiasi file in qualsiasi punto della storia senza fare checkout di quel commit.
Blame con intervalli di righe#
git blame -L 42,60 src/auth/session.tsMostra chi ha modificato per ultimo le righe 42-60. Più utile che blamare l'intero file, che è di solito opprimente.
# Mostra anche il messaggio del commit, non solo l'hash
git blame -L 42,60 --show-name src/auth/session.ts
# Ignora le modifiche di spazi bianchi (molto utile)
git blame -w -L 42,60 src/auth/session.ts
# Mostra il commit prima di quello blamato (scava più a fondo)
git log --follow -p -- src/auth/session.tsIl flag -w è importante. Senza, blame attribuirà le righe a chiunque abbia riformattato il file per ultimo, che raramente è la persona che stai cercando.
Trova una stringa in tutta la storia#
# Trova quando una funzione è stata aggiunta o rimossa
git log -S "validateSession" --oneline
# Trova quando un pattern regex è apparso
git log -G "session.*timeout" --oneline-S (il "pickaxe") trova commit dove il numero di occorrenze di una stringa è cambiato. -G trova commit dove il diff corrisponde a un regex. Entrambi sono potenti per l'archeologia — capire quando qualcosa è stato introdotto o rimosso.
Mostra cosa è cambiato tra due punti#
# Cosa è cambiato tra due branch
git diff main..feature/auth
# Cosa è cambiato su questo branch da quando è divergo da main
git diff main...feature/auth
# Elenca solo i file cambiati
git diff main...feature/auth --name-only
# Vista statistiche (file + inserimenti/eliminazioni)
git diff main...feature/auth --statDue punti vs tre punti conta. Due punti mostra la differenza tra le punte di entrambi i branch. Tre punti mostra cosa è cambiato sul lato destro da quando è divergo dal lato sinistro. Tre punti è di solito ciò che vuoi quando rivedi un feature branch.
Pulisci i file non tracciati#
# Vedi cosa verrebbe eliminato (dry run)
git clean -n
# Elimina i file non tracciati
git clean -f
# Elimina file e directory non tracciati
git clean -fd
# Elimina file non tracciati e ignorati (opzione nucleare)
git clean -fdxEsegui sempre con -n prima. git clean -fdx eliminerà i tuoi node_modules, .env, artefatti di build — tutto ciò che non è tracciato da Git. Utile per un nuovo inizio veramente pulito, ma distruttivo.
Ripristina un singolo file da un altro branch#
# Ottieni la versione di main di un file senza cambiare branch
git restore --source main -- src/config/database.tsO da un commit specifico:
git restore --source a1b2c3d -- src/config/database.tsQuesto è più pulito della vecchia sintassi git checkout main -- path/to/file, e non influenza HEAD.
Mettere tutto insieme: un workflow reale#
Ecco come appare una giornata tipica con questi strumenti:
# Mattina: controlla cosa sta succedendo
git lg
git fetch --all
# Inizia una feature
git checkout -b feature/session-refresh main
# Lavora, committa incrementalmente
git add -p # Metti in staging hunk specifici, non interi file
git commit -m "Add token refresh endpoint"
git commit -m "Add refresh token rotation"
git commit -m "Fix: handle expired refresh tokens"
git commit -m "Add integration tests"
# Interrotto: devi fixare un bug in produzione
git worktree add ../hotfix main
cd ../hotfix
# ... fixa, committa, pusha, PR mergiata ...
cd ../my-project
git worktree remove ../hotfix
# Torna al lavoro sulla feature
git commit -m "Fix edge case in token validation"
# Pronto per la PR: pulisci la storia
git rebase -i main
# Squash dei commit fix, riscrittura per chiarezza
# Push e apri PR
git push -u origin feature/session-refresh
# Qualcosa si è rotto in staging? Scopri quale commit:
git bisect start
git bisect bad HEAD
git bisect good main
git bisect run npm test
# Ops, ho fatto hard-reset della cosa sbagliata
git reflog
git reset --hard HEAD@{2}Ognuno di questi comandi richiede secondi. Insieme, risparmiano ore. Non ore ipotetiche — ore reali, ogni settimana, che altrimenti passerei a sbrogliare casini di Git, cercare manualmente bug, o perdere lavoro per i cambi di contesto.
Git è uno strumento che premia la profondità. Le basi ti portano avanti durante la giornata. Ma i comandi in questo post sono ciò che separa "uso Git" da "Git mi rende davvero più veloce." Impara incrementalmente. Scegli una nuova tecnica questa settimana e usala fino a che diventa memoria muscolare. Poi scegline un'altra.
Il tuo futuro te stesso, che fissa un bug in produzione alle 11 di sera, ti ringrazierà per conoscere git bisect.