conflitti di merge
Scopri le cause dei conflitti di merge in Git, come leggere i marcatori e come risolverli, annullarli o prevenirli con i comandi giusti.
Un conflitto di merge si verifica quando Git non riesce a riconciliare automaticamente due serie di modifiche. Accade quando due branch modificano le stesse righe dello stesso file in modi diversi, oppure quando un branch elimina un file che l'altro branch ha modificato. Poiché Git non può sapere quale versione sia quella corretta, mette in pausa il merge e lascia la decisione a te.
Questa pagina spiega perché si verificano i conflitti, come riprodurne uno, come leggere i marcatori che Git inserisce nei file e i diversi modi per risolvere, annullare o addirittura prevenire un conflitto. Il comando git merge è quello che unisce due branch ed è dove la maggior parte dei conflitti emerge.
Git gestisce il merge meglio della maggior parte dei sistemi di controllo versione e integra le modifiche automaticamente ogni volta che le due parti toccano sezioni diverse di un file. Un conflitto non è quindi un errore nel tuo flusso di lavoro — è Git che chiede una decisione umana. Quando non riesce a decidere, contrassegna il file come in conflitto e interrompe il merge in modo che nulla vada perso.

Quando si verificano i conflitti?
Non ogni merge produce un conflitto. Sapere quando è probabile che si verifichi aiuta a evitare sorprese:
- Stesse righe modificate su entrambi i lati — il caso classico. Due branch cambiano la stessa riga di
example.txtin modo diverso. - Un file modificato su un branch ed eliminato sull'altro — Git non riesce a decidere se mantenere le modifiche o rispettare l'eliminazione.
- Differenze di spazi bianchi, fine riga o codifica — modifiche che sembrano identiche possono comunque creare conflitti se i byte differiscono.
Quando i due branch toccano righe diverse o file diversi, Git li unisce automaticamente senza alcun conflitto.
Interruzioni comuni durante il merge
Git può interrompere un merge in due punti distinti e i due casi richiedono soluzioni diverse. È utile capire in quale dei due ti trovi.
Fallimento del merge all'avvio
Git si rifiuta di avviare un merge quando le modifiche non committate nella directory di lavoro o nell'area di staging verrebbero sovrascritte dai commit in arrivo. Questo non è un conflitto di contenuto — è Git che protegge il lavoro che non hai ancora committato. Per riprendere il controllo del tuo stato locale, usa git stash (metti da parte le modifiche), git commit (salvale), git checkout o git reset (scartale), poi esegui di nuovo il merge. Il messaggio appare così:
error: Your local changes to the following files would be overwritten by merge:
example.txt
Please commit your changes or stash them before you merge.
AbortingFallimento durante il merge
Un fallimento durante il merge significa che Git ha iniziato a combinare i branch ma ha incontrato un vero conflitto di contenuto tra il branch corrente e il branch che stai unendo. Il merge viene lasciato a metà in modo che tu possa risolverlo. Il messaggio appare così:
CONFLICT (content): Merge conflict in example.txt
Automatic merge failed; fix conflicts and then commit the result.Creare un conflitto di merge
Puoi riprodurre un conflitto in un repository temporaneo per esercitarti a risolverlo in sicurezza. Inizia creando un repository con un singolo file committato:
mkdir test-dir
cd test-dir
git init .
echo "some content" > example.txt
git add example.txt
git commit -m "initial commit"
[master (root-commit) a45c22d] initial commit
1 file changed, 1 insertion(+)
create mode 100644 example.txtQuesto crea una directory chiamata test-dir, inizializza un repository e committa example.txt con la riga some content. Ora abbiamo un branch (master) e un file. Successivamente, crea un secondo branch e modifica la stessa riga — è questo che rende possibile il conflitto:
git checkout -b branch_to_merge
echo "completely different content to merge later" > example.txt
git commit -m "edit the content of example.txt to make a conflict"
[branch_to_merge 4221135] edit the content of example.txt to make a conflict
1 file changed, 1 insertion(+), 1 deletion(-)git checkout -b branch_to_merge crea il branch e passa a esso. Sovrascriviamo example.txt e committiamo la modifica, quindi questo branch ora ha una versione diversa di quella riga. Torna a master e modifica lo stesso file in modo diverso:
git checkout master
Switched to branch 'master'
echo "content to add" >> example.txt
git commit -m "added content to example.txt"
[master 11ab34b] added content to example.txt
1 file changed, 1 insertion(+)Ora entrambi i branch hanno il proprio commit che tocca example.txt. Esegui il merge e Git segnala il conflitto che non riesce a risolvere da solo:
git merge branch_to_merge
Auto-merging example.txt
CONFLICT (content): Merge conflict in example.txt
Automatic merge failed; fix conflicts and then commit the result.Identificare i conflitti di merge
Oltre al messaggio stampato dal merge, git status indica esattamente quali file sono in conflitto elencandoli sotto Unmerged paths:
git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: example.txtboth modified significa che il file è stato modificato su entrambi i lati. Apri o fai cat del file per vedere i marcatori di conflitto inseriti da Git:
cat example.txt
<<<<<<< HEAD
some content
content to add
=======
completely different content to merge later
>>>>>>> branch_to_mergeLeggi i tre marcatori così:
<<<<<<< HEAD— inizio del conflitto. Tutto ciò che segue fino a=======appartiene al tuo branch corrente (master, dove puntaHEAD).=======— la linea di separazione tra le due versioni.>>>>>>> branch_to_merge— fine del conflitto. Le righe tra=======e questo marcatore provengono dal branch che stai unendo.
Per annullare completamente il merge e tornare allo stato esatto precedente all'avvio, esegui git merge --abort. Puoi leggere ulteriori informazioni sui marcatori nella pagina git merge.
Risolvere i conflitti di merge
Risoluzione manuale
Apri il file in conflitto nel tuo editor, decidi qual è il contenuto finale e elimina tutti e tre i marcatori (<<<<<<<, =======, >>>>>>>). Puoi mantenere un lato, l'altro, o una combinazione di entrambi modificata a mano. Ad esempio, mantenere entrambe le parti di contenuto produce:
some content
content to add
completely different content to merge laterUna volta che i marcatori sono stati rimossi e il contenuto è corretto, metti in staging il file con git add e committa per completare il merge:
git add example.txt
git commit -m "resolve merge conflict in example.txt"Eseguire git commit dopo un conflitto crea un merge commit che collega le storie di entrambi i branch.
Risoluzione scegliendo un solo lato
Quando vuoi semplicemente mantenere la versione di un file da un branch, non è necessario modificare manualmente. Fai checkout del lato che vuoi, poi metti in staging e committa:
git checkout --ours example.txt # keep the version from the current branch (master)
git checkout --theirs example.txt # keep the version from the incoming branch
git add example.txt--ours mantiene il contenuto del tuo branch corrente; --theirs mantiene il contenuto del branch in arrivo.
Risoluzione con uno strumento visuale
Per conflitti più grandi, git mergetool apre uno strumento di diff configurato affiancato in modo da poter risolvere i conflitti in modo interattivo invece di modificare manualmente i marcatori. Esegui git mergetool per analizzare ogni file in conflitto, oppure git mergetool example.txt per uno specifico.
Annullare un merge
Se decidi che il merge è stato un errore, o preferisci ricominciare con una directory di lavoro pulita, annullalo:
git merge --abortQuesto ripristina il tuo branch e la directory di lavoro allo stato esatto in cui si trovavano prima di eseguire git merge — nessun marcatore, nessun file parzialmente unito. Usalo ogni volta che il conflitto è più di quanto tu voglia gestire in questo momento.
Prevenire i conflitti di merge
I conflitti sono normali, ma puoi ridurre la frequenza e la gravità con cui si presentano:
- Esegui il merge o il rebase dal branch principale spesso in modo che il tuo branch non si allontani troppo da esso.
- Mantieni i commit piccoli e mirati ed evita riformattazioni generalizzate nella stessa modifica che contiene cambiamenti logici.
- Comunica in modo che due persone non riscrivano lo stesso file contemporaneamente.
- Visualizza in anteprima la sovrapposizione con git diff prima di fare il merge per vedere quali righe potrebbero entrare in conflitto.
Cheat sheet dei comandi
Questi sono i comandi a cui si ricorre più spesso quando si gestiscono i conflitti:
| Strumento | Descrizione |
|---|---|
git status | Aiuta a trovare i file in conflitto. |
git mergetool | Apre uno strumento di diff visuale per risolvere i conflitti in modo interattivo. |
git diff | Mostra le differenze tra commit, branch o file per aiutare a identificare potenziali conflitti prima del merge. |
git checkout --ours/--theirs | Sostituisce il file in conflitto con il contenuto del branch corrente o di quello in arrivo. |
git reset --mixed | Rimuove i file dallo staging ma lascia invariata la directory di lavoro. |
git merge --abort | Annulla il merge corrente e ripristina la directory di lavoro allo stato precedente all'avvio del merge. |
git reset | Reimposta l'indice per corrispondere a HEAD, aiutando a rimuovere dallo staging i file in conflitto. |