strategie di merge
Scopri le strategie di merge Git — ort, recursive, resolve, octopus, ours e subtree — più fast-forward, squash e merge espliciti, con esempi.

Strategie di merge in Git
Quando il lavoro su un branch è completo e pronto per essere unito alla linea principale di sviluppo, Git deve decidere come combinare le due storie. L'algoritmo che utilizza per farlo si chiama strategia di merge.
Una strategia di merge prende due (o più) tip di branch e produce un singolo risultato. La maggior parte delle volte non si nomina mai una strategia in modo esplicito — il comando git merge sceglie un valore predefinito sensato in base a quanti branch si stanno unendo e se le loro storie hanno divergito. Quando hai bisogno di controllo, usa -s <strategy> (e facoltativamente -X <strategy-option> per una regolazione fine):
git merge -s recursive featureQuesta pagina copre due idee correlate che è facile confondere:
- Strategie di merge (
-s): l'algoritmo che calcola l'albero unito —ort,recursive,resolve,octopus,ours,subtree. - Tipi di merge: il tipo di risultato che si ottiene — un commit di merge esplicito, un fast-forward, o uno squash.
Quasi mai si ha bisogno di scegliere una strategia manualmente. Le impostazioni predefinite sono corrette per la stragrande maggioranza dei merge; ricorri a -s o -X solo quando incappi in un problema specifico che risolvono.
Algoritmi delle strategie di merge
ort (predefinito)
git merge -s ort featureort ("Ostensibly Recursive's Twin") è la strategia di merge predefinita a due teste da Git 2.34 (2021). È una riscrittura più veloce e più corretta della vecchia strategia recursive e produce lo stesso tipo di risultato: un merge a 3 vie che gestisce i rinominamenti e unisce ricorsivamente più antenati comuni in un singolo antenato virtuale.
Poiché è il valore predefinito, si ottiene ort automaticamente quando si esegue un merge semplice:
git checkout main
git merge featurerecursive
git merge -s recursive featureLa strategia a 3 vie originale per l'unione di due branch, e il valore predefinito prima di Git 2.34. Può rilevare e seguire i rinominamenti ma non può usare copie di file rilevate. Raramente è necessario richiederla per nome ora — ort la sostituisce — ma è ancora disponibile per compatibilità.
resolve
git merge -s resolve featureresolve esegue un singolo merge a 3 vie tra esattamente due teste (il branch corrente e quello nominato). Non tenta di essere intelligente riguardo a più basi di merge, il che lo rende veloce e prevedibile, ma può eseguire merge errati attraverso una storia "criss-cross" in cui due branch sono stati uniti l'uno nell'altro in precedenza. Usalo solo quando il merge recursive/ort produce un risultato che si vuole verificare con un algoritmo più semplice.
octopus
git merge -s octopus topic-a topic-b topic-coctopus è la strategia predefinita quando si uniscono più di due branch contemporaneamente. Raccoglie diversi tip di branch in un unico commit di merge, il che è pratico per unire un insieme di branch topic indipendenti. Rifiuta deliberatamente qualsiasi merge che richiederebbe una risoluzione manuale dei conflitti — i merge octopus sono pensati per essere puliti, quindi in caso di conflitto è necessario unire i branch singolarmente.
ours
git merge -s ours obsolete-branchLa strategia ours registra un commit di merge che ha l'altro branch come genitore ma mantiene l'albero del branch corrente completamente invariato — ogni modifica dall'altro branch viene scartata. L'uso tipico è marcare un branch come "unito" per la storia (in modo che i futuri merge lo conoscano) ignorandone il contenuto effettivo, ad esempio quando si archivia un branch longevo il cui lavoro non è più desiderato.
Non confondere la strategia ours (-s ours, che scarta completamente il contenuto dell'altro branch) con l'opzione di strategia ours (-X ours, che mantiene il proprio lato solo per le righe che effettivamente generano conflitti). Si comportano in modo molto diverso.
subtree
git merge -s subtree project-bsubtree è una variante dell'algoritmo recursive/ort per il caso in cui un albero è una sottodirectory (un "subtree") dell'altro. Prima di unire, Git sposta i percorsi di un albero in modo che i due si allineino, poi unisce normalmente. Questo è il meccanismo alla base dell'incorporazione di un progetto in una sottocartella di un altro. Per il lavoro quotidiano con i subtree, il comando di livello superiore git subtree è solitamente più comodo.
Tipi di merge: come appare il risultato
La strategia decide come gli alberi vengono combinati; il tipo di merge descrive la forma della storia che ne risulta.
Merge fast-forward
Quando il branch in cui si sta eseguendo il merge non si è mosso da quando l'altro branch è stato creato, non c'è nulla da combinare — Git può semplicemente spostare in avanti il puntatore del branch all'ultimo commit. Non viene creato nessun nuovo commit e la storia rimane perfettamente lineare. Questo è il comportamento predefinito di Git ogni volta che è possibile:
git checkout main
git merge feature
# Output (when main is an ancestor of feature):
# Updating a1b2c3d..d4e5f6a
# Fast-forward
# app.js | 3 +++
# 1 file changed, 3 insertions(+)Per mantenere un registro esplicito del merge anche quando un fast-forward è possibile, si forza un commit di merge con --no-ff:
git merge --no-ff featureCommit di merge esplicito (a 3 vie)
Quando entrambi i branch hanno nuovi commit — le loro storie hanno divergito — Git crea un nuovo commit di merge con due genitori. È "esplicito" perché il commit è visibile nella storia e registra esattamente dove e quando i branch si sono uniti:
git checkout main
git merge feature
# Output (when histories diverged):
# Merge made by the 'ort' strategy.
# app.js | 5 +++++
# 1 file changed, 5 insertions(+)Se i due branch hanno modificato le stesse righe, il merge si interrompe con un conflitto che deve essere risolto manualmente — vedi conflitti di merge.
Squash merge
Uno squash merge comprime tutti i commit del branch sorgente in un unico nuovo commit sul branch corrente. Non crea un commit di merge e non registra il branch sorgente come genitore, quindi i singoli commit del branch sorgente non compaiono mai nella storia di destinazione:
git checkout main
git merge --squash feature
# Changes are staged but NOT committed yet:
git commit -m "Add feature X"Questo mantiene la storia del branch principale ordinata — un commit per funzionalità — al costo di perdere il log dettagliato dei commit del branch della funzionalità. È una politica popolare per le pull request. Per riscrivere i commit all'interno di un branch, vedi il rebase interattivo.
Opzioni di strategia (-X)
Le strategie ort/recursive accettano opzioni aggiuntive tramite il flag -X (nota la X maiuscola, separata da -s). Ad esempio, per risolvere automaticamente i conflitti a favore del proprio lato:
git merge -X ours featureLe opzioni disponibili sono:
| Opzione | Effetto |
|---|---|
ours | Risolve automaticamente gli hunk in conflitto favorendo il proprio lato. Le modifiche non in conflitto dall'altro albero vengono comunque unite. (A differenza di -s ours, che scarta completamente l'altro lato.) |
theirs | L'opposto di ours: risolve automaticamente i conflitti a favore dell'altro albero. Non esiste una strategia theirs separata, solo questa opzione. |
patience | Dedica più tempo alla corrispondenza delle righe per evitare merge errati causati da righe corrispondenti non importanti. |
diff-algorithm=<algo> | Indica al merge di usare un algoritmo diff diverso (ad es. histogram, minimal, patience). |
ignore-space-change / ignore-all-space | Ignora le differenze solo di spazi bianchi nel rilevamento dei conflitti. Le modifiche agli spazi bianchi miste a modifiche reali non vengono ignorate. |
renormalize | Esegue un check-out e check-in virtuale di tutti gli stage dei file, utile quando sono cambiati i filtri di fine riga o smudge/clean. |
no-renormalize | Disabilita l'opzione renormalize. |
no-renames | Disattiva il rilevamento dei rinominamenti durante il merge. |
find-renames=<n> | Attiva il rilevamento dei rinominamenti con una soglia di similarità di n% (predefinito 50%). |
subtree=<path> | Come la strategia subtree, ma consente di specificare il prefisso del percorso da spostare per allineare gli alberi. |
Come scegliere
Per il lavoro quotidiano non si sceglie affatto una strategia — si lascia che Git usi ort e si decide solo il risultato desiderato:
- Si vuole la storia più semplice e lineare quando possibile? Basta
git merge(i fast-forward avvengono automaticamente). - Si vuole che ogni merge sia registrato come un commit? Aggiungere
--no-ff. - Si vuole un unico commit pulito per funzionalità su
main? Usare--squash. - Si stanno unendo diversi branch topic completati contemporaneamente? Il semplice
git merge a b cusaoctopus.
Se si preferisce ripetere i propri commit sopra il branch di destinazione invece di fare un merge, vedi git rebase. Per portare un singolo commit da un altro branch, usa git cherry-pick.