Confronto

Git Flow, GitHub Flow e trunk-based: quale workflow Git scegliere

Aggiornato: · Skillaria.Top

Git Flow è un modello di branching con i rami di lunga durata main e develop e i rami di supporto feature, release e hotfix; funziona bene per i prodotti con versioni pianificate. Per i servizi web con rilasci frequenti di solito è più semplice GitHub Flow (rami brevi da main e pull request) oppure il trunk-based development con i feature flag.

Che cos'è un workflow Git e perché la sua scelta conta

Il workflow Git è l'accordo del team su quali rami esistono, da dove vengono creati, dove vengono uniti e che cosa si considera pronto per il rilascio. Git stesso non impone nulla di tutto questo, quindi senza un accordo ognuno lavora a modo proprio: dopo un mese il repository contiene decine di rami, non è chiaro che cosa sia stato distribuito e le fusioni si trasformano in conflitti che durano ore.

I modelli più noti sono Git Flow, GitHub Flow (una variante del Feature Branch Workflow) e trunk-based development. Non si tratta di «vecchio» e «nuovo»: sono strumenti per condizioni diverse, cioè per quanto spesso rilasciate, per quante versioni del prodotto mantenete in parallelo e per quanto sono solidi i vostri test automatici.

Git Flow: il modello con i rami develop e release

Git Flow è stato descritto da Vincent Driessen nel 2010. Il modello si basa su due rami di lunga durata:

  • main (nei progetti più vecchi si chiama master) — contiene solo il codice rilasciato; ogni commit su questo ramo corrisponde a una release ed è marcato con un tag;
  • develop — il ramo di integrazione, dove confluiscono le funzionalità pronte per la prossima release.

Esistono inoltre tre tipi di rami temporanei:

RamoCreato daUnito inScopo
feature/*developdevelopSviluppo di una nuova funzionalità
release/*developmain e developStabilizzazione della versione: correzioni di bug, numero di versione, documentazione
hotfix/*mainmain e developCorrezione urgente di una versione già rilasciata

Git Flow con i comandi Git puri

Il ciclo di una funzionalità e di una release ha questo aspetto:

# funzionalità
git switch develop
git switch -c feature/cart-discounts
# ... commit ...
git switch develop
git merge --no-ff feature/cart-discounts
git branch -d feature/cart-discounts

# release
git switch -c release/1.4.0 develop
# ... modifiche di versione e correzioni di bug ...
git switch main
git merge --no-ff release/1.4.0
git tag -a v1.4.0 -m "Release 1.4.0"
git switch develop
git merge --no-ff release/1.4.0
git branch -d release/1.4.0

Il flag --no-ff è essenziale in questo modello: crea un merge commit anche dove sarebbe possibile un fast-forward, così nella cronologia resta visibile quali commit appartenevano a una stessa funzionalità o release.

L'estensione git-flow

Per non digitare a mano queste sequenze esiste l'utility git-flow (oggi di solito si installa il fork mantenuto, che nei gestori di pacchetti si chiama spesso git-flow-avh). Aggiunge i sottocomandi:

git flow init -d
git flow feature start cart-discounts
git flow feature finish cart-discounts
git flow release start 1.4.0
git flow release finish 1.4.0
git flow hotfix start 1.4.1
git flow hotfix finish 1.4.1

git flow init chiede i nomi dei rami e dei prefissi (con -d accetta i valori predefiniti), mentre i comandi finish eseguono tutte le fusioni necessarie, creano il tag ed eliminano il ramo temporaneo. Molti client grafici per Git supportano Git Flow tramite pulsanti.

Pro e contro di Git Flow

Pro: struttura chiara, stato del codice sempre leggibile (main è ciò che hanno i clienti, develop è la versione successiva), una fase dedicata alla stabilizzazione della release e una gestione comoda degli hotfix. Il modello si adatta bene alle app mobili, al software installato localmente, alle librerie e al firmware, cioè dove la release passa la revisione dello store o viene installata dal cliente.

Contro: molti rami e molte fusioni, i rami di funzionalità di lunga durata accumulano divergenze da develop e i doppi merge di release e hotfix si dimenticano facilmente. Per un servizio web distribuito più volte al giorno, i rami di release diventano burocrazia. Lo stesso autore del modello, nel 2020, ha aggiunto una nota al suo articolo: per il software distribuito in modo continuo conviene valutare approcci più semplici, come GitHub Flow.

GitHub Flow: rami brevi e pull request

GitHub Flow è una versione semplificata del Feature Branch Workflow. Le regole stanno in pochi punti:

  1. main è sempre in uno stato funzionante e pronto per il deploy.
  2. Ogni lavoro si svolge in un ramo creato da main, con un nome chiaro.
  3. Il ramo viene inviato spesso al server e si apre una pull request (merge request) per la discussione.
  4. Dopo la revisione e il CI verde, il ramo viene unito a main.
  5. Tutto ciò che entra in main viene distribuito subito.
git switch main
git pull
git switch -c feature/new-payment
# ... commit con messaggi chiari ...
git push -u origin feature/new-payment
# apriamo la pull request, passiamo la revisione e il CI, uniamo in main

Pro: un solo ramo di lunga durata, poche regole, percorso rapido dall'idea alla produzione, revisione del codice integrata. Contro: non c'è un posto naturale per mantenere più versioni o per stabilizzare una release, e senza buoni test automatici la frase «main è sempre pronto» resta un desiderio.

Un approccio simile con rami aggiuntivi per gli ambienti (per esempio staging e production) viene talvolta chiamato GitLab Flow. È utile quando il deploy in produzione non è immediato, ma avviene secondo un calendario o dopo una verifica manuale.

Trunk-based development

Nel trunk-based development tutti gli sviluppatori integrano le modifiche in un unico ramo (il trunk, cioè main) di frequente, almeno una volta al giorno. I rami, se esistono, vivono ore e non settimane. Le funzionalità non ancora complete entrano in main ma restano nascoste dietro un feature flag:

if (featureFlags.isEnabled("new-checkout", user)) {
    renderNewCheckout();
} else {
    renderOldCheckout();
}

Il vantaggio principale è la quasi totale assenza di conflitti pesanti: le modifiche sono piccole e vengono unite quando tutti ricordano ancora il contesto. Il modello segue alla lettera l'idea di integrazione continua e si adatta bene ai team maturi, con un CI veloce e una buona copertura di test. I rami di release sono ammessi, ma si creano dal trunk poco prima del rilascio, e le correzioni vengono applicate prima al trunk e poi trasferite con cherry-pick.

Contro: servono disciplina e infrastruttura, cioè test rapidi, flag e monitoraggio. Per chi è alle prime armi fa paura committare in main ogni giorno, e un CI debole trasforma il trunk in un ramo che si rompe in continuazione. Anche i flag vanno rimossi in tempo, altrimenti il codice si riempie di rami morti.

Confronto tra Git Flow, GitHub Flow e trunk-based

CriterioGit FlowGitHub FlowTrunk-based
Rami di lunga duratamain e developmainmain (trunk)
Durata di un ramo di funzionalitàGiorni o settimaneGiorniOre o nessun ramo
Frequenza dei rilasciVersioni pianificateMan mano che si uniscono le modificheContinua
Supporto di più versioniSì, con tag e hotfixScomodoTramite rami di release
Requisiti su CI e testMediAltiMolto alti
Rischio di conflitti pesantiAltoMedioBasso
Curva di apprendimentoBisogna imparare le regoleMinimaServe una cultura del team e dei feature flag
Progetto tipicoApp mobile, libreria, software installatoServizio web, open source, startupProdotto grande con deploy frequenti

Come scegliere il workflow Git per il team

Rispondete a qualche domanda:

  • Dovete mantenere più versioni rilasciate? Se i clienti usano la 2.x e la 3.x e entrambe hanno bisogno di correzioni, valutate Git Flow o il trunk-based con rami di release.
  • Con quale frequenza distribuite? Una volta al trimestre: Git Flow non è un problema. Ogni giorno: scegliete GitHub Flow o trunk-based.
  • Quanto vi fidate dei test? Se il CI è lento e la copertura è scarsa, cominciate con GitHub Flow, con revisione obbligatoria, e accorciate gradualmente i rami.
  • Quanto è grande il team e quanta esperienza ha? Per un team piccolo o con poca esperienza la scelta più semplice è GitHub Flow: una sola regola, «main è sempre funzionante», e una pull request per ogni modifica.

Consiglio pratico: non adottate Git Flow solo perché è lo standard. Partite dal processo più semplice che risolve i vostri problemi e complicatelo solo quando emerge un bisogno concreto, per esempio una fase di stabilizzazione o il supporto delle versioni precedenti.

Regole valide per qualsiasi workflow

  • Proteggete main sul server: niente push diretto, revisione obbligatoria e CI verde prima della fusione.
  • Concordate i nomi dei rami: feature/, fix/, hotfix/ più il numero del task.
  • Tenete i rami corti: più un ramo vive, più è doloroso unirlo. Aggiornate regolarmente il vostro ramo con main tramite merge o rebase.
  • Scegliete una strategia di fusione per le pull request (merge commit, squash o rebase) e usatela in modo uniforme.
  • Marcate le release con tag annotati secondo il versionamento semantico.

Hotfix in ogni modello: un bug, tre processi

La differenza tra gli approcci si vede meglio su una correzione urgente in produzione.

In Git Flow si crea hotfix/1.4.1 da main; dopo la correzione il ramo viene unito a main con il tag v1.4.1 e, obbligatoriamente, a develop (o alla release in corso, se esiste). Dimenticare la seconda fusione è l'errore classico: nella release successiva il bug «ritorna».

git switch -c hotfix/1.4.1 main
git commit -am "Correggere il calcolo dello sconto"
git switch main && git merge --no-ff hotfix/1.4.1
git tag -a v1.4.1 -m "Hotfix 1.4.1"
git switch develop && git merge --no-ff hotfix/1.4.1

In GitHub Flow l'hotfix è un normale ramo breve da main con una pull request a cui si dà la priorità nella revisione. Dopo la fusione va in produzione con lo stesso deploy automatico; non esiste un processo separato.

Nel trunk-based la correzione viene prima committata nel trunk e, se in produzione è attiva una release branch, il commit viene portato lì con git cherry-pick -x <hash>. Il flag -x aggiunge al messaggio un riferimento al commit originale, così il collegamento resta visibile nella cronologia. Se invece il problema riguarda una funzionalità nuova, spesso basta disattivare il suo feature flag senza toccare il codice.

Come rispondere al colloquio alla domanda «quale workflow Git avete usato?»

L'intervistatore non verifica la conoscenza dei nomi, ma la comprensione dei compromessi. Una buona risposta nomina il modello, descrive come sono organizzati i rami e la revisione e spiega perché era adatto al progetto. Per esempio: «Abbiamo usato GitHub Flow: rami creati da main, pull request con revisione obbligatoria e CI, deploy automatico dopo la fusione. Non abbiamo adottato Git Flow perché rilasciavamo più volte a settimana e i rami di release avrebbero solo rallentato il lavoro». Un bonus è raccontare che cosa cambiereste se il processo crescesse.

Git Flow è un modello rigoroso con i rami develop, release e hotfix, adatto ai prodotti con versioni pianificate e al supporto di più release. GitHub Flow è più semplice e va bene per la maggior parte dei progetti web, mentre il trunk-based development offre la massima velocità ma richiede un CI maturo e i feature flag. Scegliete il workflow Git in base alla frequenza dei rilasci e alla qualità dei test, non per moda. Tutti e tre gli approcci, con le loro pratiche e gli errori tipici, li analizziamo in pratica nel corso di Git.

Domande frequenti

Git Flow è ormai superato?

No, ma il suo campo di applicazione si è ridotto. Per i servizi web con deploy continuo è eccessivo, mentre per le app mobili, le librerie e il software installato resta comodo.

Serve il ramo develop se i rilasci sono frequenti?

Di solito no: develop duplica main e aggiunge una fusione in più. Con rilasci frequenti bastano main e rami di funzionalità brevi.

Si può usare rebase in Git Flow?

Sì, si può aggiornare develop nel proprio ramo di funzionalità non ancora pubblicato o personale tramite rebase, ma le fusioni finali in develop e main, nel modello classico, si fanno con merge --no-ff per conservare la struttura della cronologia.

Perché un feature flag è meglio di un ramo di funzionalità di lunga durata?

Il codice viene integrato in main subito e non diverge dal resto della base di codice; l'attivazione della funzionalità diventa una decisione separata, che si può annullare senza fare rollback del codice.

Come passare da Git Flow a GitHub Flow?

Completate le release in corso, unite develop in main, configurate il CI per distribuire da main ed eliminate develop. Da quel momento create i nuovi rami solo a partire da main.

Impara l’argomento in video

Questo argomento è trattato in modo approfondito, con pratica, nel corso «Git da zero a sviluppatore sicuro».

Vai al corso