Merge è un'operazione di unione di branch in Git che combina le modifiche da due diverse linee di sviluppo in un branch di destinazione. A differenza di rebase, merge preserva la cronologia completa di ramificazione creando un commit di merge speciale con due genitori. Secondo la documentazione ufficiale di Git (2026), merge è il modo più sicuro per unire i branch perché non riscrive la cronologia e permette di tracciare quando e quali branch sono stati uniti. È la scelta standard per l'unione nei branch pubblici come main, develop e release.
Punti chiave
Merge è il comando git merge che combina le modifiche dal branch specificato in quello corrente. Git trova l'antenato comune (commit base), calcola il diff di ciascun branch rispetto all'antenato e crea un commit di merge contenente l'insieme combinato di modifiche. Il risultato è che il branch di destinazione riceve tutte le modifiche dal branch unito.
Sintassi: mentre ti trovi nel branch di destinazione (es. main), esegui git merge feature. Git crea automaticamente un commit di merge se non ci sono conflitti. Il messaggio predefinito del commit di merge è: “Merge branch 'feature' into main”. Puoi cambiare il messaggio usando il flag -m o modificarlo nell'editor aperto.
Merge è un'operazione non distruttiva. A differenza di rebase, merge non tocca i commit esistenti: mantengono gli stessi hash, autori e date. Questo rende merge l'unico modo sicuro per unire branch su cui più sviluppatori lavorano contemporaneamente. Se qualcosa va storto, merge può essere annullato con git merge --abort.
# Passare al branch di destinazione
git checkout main
# Unire il branch feature
git merge feature
# Risultato — commit di merge con due genitori
git log --oneline --graph
# Merge con messaggio personalizzato
git merge feature -m "feat: integrate authentication module"
Git supporta tre modalità di unione che vengono scelte in base al risultato desiderato. Il merge regolare (predefinito) crea un commit di merge. Lo squash merge combina tutti i commit del branch feature in uno solo. Fast-forward sposta il puntatore del branch senza creare un commit, se possibile. La scelta della modalità dipende dal flusso di lavoro del team e dalle regole della cronologia.
Merge regolare (--no-ff) — crea un commit di merge anche se l'unione potrebbe essere eseguita come fast-forward. Raccomandato per il branch main: un commit di merge marca chiaramente il punto di integrazione della funzionalità e permette di annullare facilmente tutte le modifiche del branch feature con un singolo revert del commit di merge. GitHub usa questa modalità di default quando unisce PR tramite il pulsante Merge.
Squash merge (--squash) — raccoglie tutti i commit del branch feature in un unico commit nel branch di destinazione. Utile quando la cronologia di bozza del branch feature non deve inquinare main. Svantaggio: si perde il collegamento con i commit originali — non è possibile vedere come la funzionalità è stata sviluppata passo dopo passo. GitHub usa questa modalità quando si seleziona “Squash and merge” in un PR.
Fast-forward (--ff) — se il branch di destinazione non ha nuovi commit da quando il branch feature si è diramato, Git sposta semplicemente il puntatore in avanti senza creare un commit di merge. La cronologia rimane lineare. Il flag --no-ff forza un commit di merge, mentre --ff-only darà errore se fast-forward non è possibile.
# Forzare commit di merge (raccomandato per main)
git merge --no-ff feature
# Squash merge — tutti i commit in uno
git merge --squash feature
git commit -m "feat: add authentication"
# Fast-forward solo se possibile
git merge --ff-only feature
# Annullare merge conflittuale
git merge --abort
Le strategie di unione determinano l'algoritmo che Git usa per combinare le modifiche. Ogni strategia è adatta a scenari diversi. Git seleziona automaticamente la strategia appropriata, ma gli sviluppatori possono specificarla esplicitamente con il flag --strategy. Comprendere le strategie aiuta a prevedere il comportamento di Git durante unioni complesse.
Recursive — la strategia predefinita per unire due branch. Git trova l'antenato comune, calcola le modifiche in ciascun branch e le unisce. Se viene trovato un antenato comune, recursive gestisce correttamente le ridenominazioni e le aggiunte di file. Durante i conflitti, recursive può usare opzioni aggiuntive: ours (scegliere automaticamente la nostra versione) e theirs (scegliere la loro versione).
Octopus — per unire più di due branch contemporaneamente: git merge feature1 feature2 feature3. Octopus non supporta la risoluzione dei conflitti — tutti i conflitti devono essere risolti prima di invocare il comando. Viene usato raramente, principalmente per unire più branch indipendenti che non entrano in conflitto (es. moduli diversi).
| Strategia | Numero di branch | Risoluzione conflitti |
|---|---|---|
| Recursive | 2 | Automatica + opzioni ours/theirs |
| Octopus | 3+ | No — tutti i conflitti devono essere risolti in anticipo |
| Ours | Qualsiasi | Sceglie sempre la nostra versione, ignora le modifiche altrui |
| Subtree | 2 | Per unioni di sottoalberi (subtree merge) |
Ours — una strategia speciale che ignora completamente le modifiche dal branch unito e mantiene il contenuto attuale del branch di destinazione. Viene creato un commit di merge, ma il contenuto rimane invariato. Utile quando devi registrare nella cronologia il fatto dell'unione ma rifiutare effettivamente tutte le modifiche dall'altro branch.
Il conflitto di merge si verifica quando le stesse righe di un file sono state modificate diversamente in entrambi i branch. Git non può determinare automaticamente quale versione è corretta e mette in pausa il merge. Un conflitto può anche sorgere quando un file viene rinominato in un branch e modificato in un altro, o quando lo stesso file viene contemporaneamente eliminato e modificato.
Processo di risoluzione: Git segna i file in conflitto con marcatori. Il file mostra sezioni con <<<<<<< HEAD (la nostra versione), ======= (separatore) e >>>>>>> feature (la loro versione). Lo sviluppatore modifica manualmente la sezione in conflitto, seleziona le righe desiderate da entrambe le versioni, rimuove i marcatori, salva il file e lo aggiunge all'indice con git add.
Per la risoluzione visiva dei conflitti, Git supporta mergetool — uno strumento di confronto esterno. Mergetool popolari: Meld, KDiff3, Beyond Compare, VS Code (editor di conflitti integrato). Mergetool mostra tre pannelli: la nostra versione, la loro versione e il risultato. Lo sviluppatore seleziona visivamente i blocchi di codice da includere nel file finale.
# Avviare merge e rilevare conflitto
git merge feature
# CONFLITTO (contenuto): Conflitto di merge in src/main.swift
# Controllare i file in conflitto
git status
# Aprire mergetool visivo
git mergetool
# Dopo la risoluzione — add e commit
git add src/main.swift
git commit
# Annullare merge
git merge --abort
Merge è preferibile a rebase in diverse situazioni chiave. Prima: quando si lavora con branch pubblici accessibili ad altri sviluppatori. Merge non riscrive la cronologia, quindi i colleghi possono sincronizzarsi in sicurezza. Fare rebase su un branch pubblico crea una cronologia divergente e causa conflitti a tutti coloro che hanno già i vecchi commit.
Seconda situazione: quando si completa un branch feature. La maggior parte dei team preferisce merge (con il flag --no-ff) in main per registrare il momento di integrazione della funzionalità. Questo semplifica la navigazione della cronologia e permette di annullare facilmente un'intera funzionalità con un singolo git revert del commit di merge. GitHub Flow offre di default tre opzioni di merge: merge semplice, squash merge e rebase merge.
Terza situazione: quando si lavora con una pull request revisionata. GitHub e GitLab offrono un pulsante di merge con diverse opzioni. Merge (Create a merge commit) — cronologia completa con un commit di merge. Squash and merge — cronologia pulita senza dettagli di sviluppo. Rebase and merge — cronologia lineare senza commit di merge, ma con riscrittura dei commit. La scelta dipende dalle regole del team.
Prima regola: essere sempre sull'ultima versione del branch di destinazione prima di unire. Esegui git checkout main && git pull prima di unire il branch feature. Questo minimizza i conflitti e garantisce che il commit di merge contenga tutte le ultime modifiche. Se il branch di destinazione è avanzato significativamente, esegui prima git merge main all'interno del branch feature per risolvere i conflitti nel suo contesto.
Seconda regola: testare il codice dopo il merge. L'unione può cambiare il comportamento anche senza conflitti. La pipeline CI/CD dovrebbe eseguire test sul commit di merge prima dell'invio in produzione. Alcuni team usano merge gates — controlli obbligatori che bloccano il merge fino al loro superamento.
Terza regola: documentare i commit di merge. Il messaggio standard “Merge branch 'feature' into main” è poco utile. Si consiglia di aggiungere una descrizione di ciò che è stato unito: “Merge authentication module: login, registration, password recovery”. Questo semplifica l'analisi della cronologia e la ricerca di regressioni. Nei progetti grandi, i commit di merge vengono generati automaticamente dal titolo del PR.
Domande frequenti
Unire significa eseguire git merge per combinare le modifiche da un branch all'altro. Il risultato è un commit di merge che registra l'evento di unione e contiene le modifiche di entrambi i branch. Questo è il modo principale per integrare i branch feature in main, develop o release in Git Flow.
Squash merge combina tutti i commit del branch feature in un unico commit nel branch di destinazione, perdendo la cronologia di sviluppo intermedia. Il merge regolare crea un commit di merge preservando tutti i commit del branch feature. Squash merge dà una cronologia pulita ma non permette di tracciare lo sviluppo passo dopo passo della funzionalità.
Apri il file in conflitto, trova le sezioni con i marcatori <<<<<<< HEAD e >>>>>>>. Modifica il contenuto, mantenendo le righe necessarie da entrambe le versioni, rimuovi i marcatori. Salva il file, esegui git add e git commit. Puoi usare git mergetool per la risoluzione visiva.
Merge è sempre usato per i branch pubblici (main, develop, release) perché non riscrive la cronologia. Rebase viene applicato nei branch feature personali prima della loro pubblicazione. Una volta che un branch è diventato parte del repository condiviso e i colleghi vi hanno avuto accesso, è consentito solo merge.
Prima del completamento del merge (durante un conflitto) — git merge --abort annulla completamente il merge. Dopo il completamento — git revert <merge-commit-hash> -m 1 crea un commit di annullamento. Il flag -m 1 specifica quale branch genitore mantenere (quello di destinazione). Git revert è più sicuro di git reset per i branch pubblicati.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche