Lezione 10 di 35

Git 10

🔀 Git & GitHub · Modulo 2 · Lezione 10

Gestire i conflitti di merge ⚔️

Come nascono · Come si presentano · Marker <<< === >>> · Risoluzione passo per passo · Esercitazione pratica

⏱️ Durata stimata: 75–90 min 📊 Livello: Intermedio (Modulo 2) 🔧 Strumenti: Terminale · Editor di testo (VS Code consigliato) 📋 Prerequisiti: Lezioni 01–09 (sai fare merge)

⚔️ I conflitti di merge sono la cosa che spaventa di più i principianti. Ma una volta capito il meccanismo, diventano routine — un piccolo inconveniente che si risolve in pochi minuti. Un conflitto non è un errore: è Git che ti dice “ho trovato due modifiche sulla stessa riga e non so quale tenere — decidi tu”. In questa lezione impari a riconoscere, leggere e risolvere i conflitti con sicurezza.

🎯 Obiettivi della lezione

  • Capire quando e perché nasce un conflitto di merge
  • Riconoscere lo stato “merge in corso” nel terminale
  • Leggere i marker di conflitto nel file (<<<, ===, >>>)
  • Risolvere un conflitto manualmente: scegliere, combinare, pulire
  • Completare il merge dopo la risoluzione
  • Annullare un merge in corso con git merge --abort
  • Usare VS Code per risolvere i conflitti visualmente

📖 1. Teoria

1.1 — Quando nasce un conflitto

Nella Lezione 09 hai visto che Git è bravissimo a combinare automaticamente le modifiche di due branch. Ma c’è un caso in cui non può farcela da solo: quando due branch hanno modificato la stessa riga dello stesso file in modo diverso.

✅ Nessun conflitto
Main modifica la riga 5 di style.css
Feature modifica la riga 20 di style.css

Git combina automaticamente: righe diverse dello stesso file, nessun problema.
⚔️ Conflitto!
Main modifica la riga 5: color: blue;
Feature modifica la riga 5: color: red;

Git non può scegliere — entrambe le versioni sono valide. Chiede a te di decidere.
Le 4 situazioni durante un merge
Situazione Cosa fa Git Conflitto?
Un file è stato modificato solo su un branch Prende la versione modificata No
Un file è stato modificato su entrambi, ma in righe diverse Combina automaticamente le modifiche No
Un file nuovo è stato creato solo su un branch Aggiunge il file No
Un file è stato modificato su entrambi, nelle stesse righe Si ferma e chiede aiuto a te ⚔️ Sì

📌 I conflitti sono rari nella pratica. Se ogni persona lavora su file o sezioni diverse, i merge vanno lisci automaticamente. I conflitti nascono quando due persone toccano lo stesso punto dello stesso file — cosa che succede soprattutto con file centrali come index.html o style.css che tutti modificano. La prevenzione migliore è comunicare con il team: “sto lavorando su style.css — voi non toccatelo per adesso”.

1.2 — Cosa succede quando Git trova un conflitto

Quando fai git merge e Git trova un conflitto, succedono tre cose:

$ git merge feature/stili
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
Automatic merge failed; fix conflicts and then commit the result.
1
Git si ferma a metà merge. Non crea il merge commit — il merge è “in corso” ma non completato. Lo vedi dal prompt che mostra (main|MERGING).
2
Git segna i conflitti dentro i file. I file con conflitto contengono marker speciali (<<<<<<<, =======, >>>>>>>) che mostrano le due versioni in competizione.
3
Git aspetta che tu risolva. Devi: aprire i file, scegliere quale versione tenere (o combinarle), rimuovere i marker, fare git add e poi git commit per completare il merge.
# Il prompt cambia per indicare che sei in stato di merge
studente@pc ~/progetto (main|MERGING)$

# git status mostra i file in conflitto
$ git status
On branch main
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: style.css

1.3 — I marker di conflitto: anatomia

Quando apri un file con un conflitto, trovi dei marker che separano le due versioni. Ecco come si presentano:

body {
margin: 0;
<<<<<<< HEAD
color: blue;
font-size: 16px;
=======
color: red;
font-weight: bold;
>>>>>>> feature/stili
}
Marker Significato
<<<<<<< HEAD Inizio del conflitto. Qui sotto c’è la versione del branch corrente (HEAD = main, il branch su cui sei).
(righe in mezzo — sezione HEAD) Il codice come lo ha scritto il branch corrente (main).
======= Separatore. Divide la versione di HEAD dalla versione dell’altro branch.
(righe in mezzo — sezione feature) Il codice come lo ha scritto il branch in arrivo (feature/stili).
>>>>>>> feature/stili Fine del conflitto. Dopo questa riga il file torna normale.

📌 Con merge.conflictstyle diff3 (configurato nella Lezione 02) vedrai anche una terza sezione ||||||| base che mostra la versione originale (prima delle modifiche di entrambi i branch). Questo aiuta enormemente a capire cosa è cambiato. Se non l’hai configurato: git config --global merge.conflictstyle diff3.

1.4 — Come risolvere un conflitto: procedura

PASSO 1
Identifica
Esegui git status per vedere quali file hanno conflitti (appaiono come both modified).
PASSO 2
Apri
Apri il file nell’editor. Cerca i marker <<<<<<<. Ogni blocco di marker è un conflitto da risolvere.
PASSO 3
Decidi
Per ogni conflitto, scegli una delle opzioni: tenere HEAD, tenere feature, tenere entrambi (combinare), o scrivere qualcosa di completamente nuovo.
PASSO 4
Pulisci
Rimuovi tutti i marker (<<<, ===, >>>). Il file deve contenere solo codice valido — nessun marker deve restare.
PASSO 5
Completa
Salva il file, fai git add file per marcarlo come risolto, poi git commit per completare il merge.

1.5 — Le 4 scelte possibili per ogni conflitto

Per ogni blocco di conflitto, hai 4 opzioni. Ecco come si presenta il file dopo la tua scelta (tutti i marker rimossi):

Opzione A: tieni HEAD (main)
body {
  margin: 0;
  color: blue;
  font-size: 16px;
}
Opzione B: tieni feature
body {
  margin: 0;
  color: red;
  font-weight: bold;
}
Opzione C: tieni entrambi
body {
  margin: 0;
  color: blue;
  font-size: 16px;
  font-weight: bold;
}
Opzione D: riscrivi
body {
  margin: 0;
  color: navy;
  font-size: 18px;
  font-weight: 600;
}
L’opzione D è la più flessibile: puoi scrivere qualcosa di completamente diverso da entrambe le versioni. L’importante è che il codice risultante sia corretto e che i marker siano stati rimossi.

1.6 — Completare o annullare il merge

✅ Completare il merge
# Dopo aver risolto i conflitti:
$ git add style.css
$ git commit
git add marca il file come “risolto”. git commit (senza -m) apre l’editor con un messaggio di merge predefinito. Salva e chiudi per completare.
🚫 Annullare il merge
$ git merge –abort
Se il conflitto è troppo complicato o non vuoi procedere adesso, --abort annulla tutto e riporta il repository allo stato precedente al merge. Nessuna modifica viene persa — è come se il merge non fosse mai iniziato.

⚠️ Errore critico: lasciare i marker nel file. Se fai git add e git commit senza aver rimosso i marker <<<<<<<, Git non protesta — committa il file così com’è, marker inclusi. Il risultato è codice rotto con caratteri strani nel mezzo. Sempre verifica che i marker siano stati rimossi prima di completare il merge.

1.7 — VS Code: risoluzione visuale

VS Code riconosce automaticamente i conflitti e li evidenzia con colori e pulsanti cliccabili:

🟢
Accept Current Change
Tieni la versione di HEAD (main). Equivale all’Opzione A.
🔵
Accept Incoming Change
Tieni la versione del branch in arrivo (feature). Equivale all’Opzione B.
🟣
Accept Both Changes
Tieni entrambe le versioni, una dopo l’altra. Equivale all’Opzione C.
🟠
Modifica manuale
Ignora i pulsanti, cancella i marker a mano e scrivi il codice che vuoi. Equivale all’Opzione D.

📌 I pulsanti di VS Code sono comodi ma non pensano per te. “Accept Both” ad esempio mette le due versioni una dopo l’altra — che spesso produce codice duplicato o non funzionante. Leggi sempre il risultato dopo aver cliccato un pulsante e verifica che il codice abbia senso.

1.8 — Riepilogo: il flusso completo di un merge con conflitto

1. MERGE
git merge feature
CONFLICT!
2. IDENTIFICA
git status
both modified: file
3. APRI E RISOLVI
Apri il file
Scegli / combina
Rimuovi marker
↓
4. MARCA RISOLTO
git add file
5. COMPLETA
git commit
OPPURE: ANNULLA
git merge –abort

💻 2. Esempi guidati

Esempio A — Creare un conflitto (passo per passo)

# === SETUP: un file con una riga che modificheremo su entrambi i branch ===
$ mkdir lab-conflitto && cd lab-conflitto && git init
$ cat > style.css <<EOF
body {
margin: 0;
color: black;
}
EOF
$ git add . && git commit -m “Setup: CSS base con colore nero”

# === BRANCH FEATURE: cambia colore in rosso ===
$ git switch -c feature/stili
$ sed -i ‘s/color: black/color: red/’ style.css # macOS: sed -i ‘’ ‘s/…’
$ git add . && git commit -m “Cambiato colore in rosso”

# === MAIN: cambia colore in blu (stessa riga!) ===
$ git switch main
$ sed -i ‘s/color: black/color: blue/’ style.css
$ git add . && git commit -m “Cambiato colore in blu”

# === MERGE: conflitto! ===
$ git merge feature/stili
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
Automatic merge failed; fix conflicts and then commit the result.

Esempio B — Risolvere il conflitto manualmente

# Passo 1: verifica lo stato
$ git status
Unmerged paths:
both modified: style.css

# Passo 2: guarda il file con conflitto
$ cat style.css
body {
margin: 0;
<<<<<<< HEAD
color: blue;
=======
color: red;
>>>>>>> feature/stili
}

# Passo 3: risolviamo — vogliamo “navy” (né blue né red)
$ cat > style.css <
body {
margin: 0;
color: navy;
}
EOF

# Passo 4: marca come risolto e completa il merge
$ git add style.css
$ git commit -m “Merge feature/stili: scelto colore navy come compromesso”

# Verifica
$ git log –oneline –graph
* a1b2c3d (HEAD -> main) Merge feature/stili: scelto colore navy come compromesso
|
| * d4e5f6a (feature/stili) Cambiato colore in rosso
* | b7c8d9e Cambiato colore in blu
|/
* e4f5a6b Setup: CSS base con colore nero
Conflitto risolto! Il merge commit contiene la nostra scelta (navy). La storia mostra chiaramente i due branch e il punto di ricongiungimento.

Esempio C — Annullare un merge in corso

# Simuliamo lo stesso conflitto (ricrea il repo o usa un altro file)
# Siamo nello stato (main|MERGING) con un conflitto aperto…

# “Troppo complicato, ci penso dopo”
$ git merge –abort

$ git status
On branch main
nothing to commit, working tree clean
Tutto cancellato — come se il merge non fosse mai iniziato. I file sono tornati allo stato di main prima del merge. Puoi riprovare quando vuoi.

🏋️ 3. Esercizi

🟢 Esercizio 1 — Il tuo primo conflitto (Facile)

a) Segui l’Esempio A per creare un conflitto. Usa un file titolo.txt con il testo “Titolo: Progetto originale”.
b) Sul branch feature, cambia in “Titolo: Progetto Alfa”.
c) Su main, cambia in “Titolo: Progetto Beta”.
d) Fai il merge. Osserva l’errore.
e) Apri il file e leggi i marker. Cosa c’è nella sezione HEAD? E nella sezione feature?
f) Risolvi scegliendo “Titolo: Progetto Alfa-Beta”.
g) Rimuovi i marker, fai git add e git commit. Verifica con git log --graph.

🔵 Esercizio 2 — Conflitto multiplo (Medio)

a) Crea un repo con un file index.html di 10 righe (una struttura HTML base).
b) Su feature/header: modifica il <title> (riga ~4) e aggiungi un <header> (riga ~7).
c) Su main: modifica lo stesso <title> con un testo diverso e aggiungi un <nav> nello stesso punto del <header>.
d) Fai il merge. Quanti blocchi di conflitto ci sono? (Probabilmente 2)
e) Risolvi entrambi: per il title, scegli il testo di feature. Per header/nav, tieni entrambi (header sopra, nav sotto).
f) Completa il merge e verifica che il file sia valido HTML.

🟠 Esercizio 3 — Abort e riprova (Medio)

a) Crea un conflitto (come nell’Esercizio 1).
b) Quando Git mostra il conflitto, non risolverlo. Esegui git merge --abort.
c) Verifica con git status che sei tornato a “clean”.
d) Il file ha ancora i marker? (No — abort ripristina tutto.)
e) Ora rifai il merge. Questa volta risolvilo scegliendo la versione di HEAD (main).
f) Completa il merge. Verifica il risultato.

🟣 Esercizio 4 — Simulazione lavoro di gruppo (Difficile)

Simula una giornata di lavoro in team dove si verificano conflitti:

a) Crea un repo con index.html, style.css, README.md. Commit iniziale.
b) “Marco” su feature/hero: modifica il <body> di index.html aggiungendo un hero section. Modifica style.css aggiungendo stili per .hero. 2 commit.
c) “Giulia” su feature/intro (partendo da main): modifica lo stesso punto del <body> di index.html aggiungendo un paragrafo introduttivo. Modifica style.css nella stessa zona aggiungendo stili per p. 2 commit.
d) Torna su main e fai merge di feature/hero (nessun conflitto — main non è cambiato).
e) Ora fai merge di feature/intro. Conflitto! Entrambi hanno toccato le stesse zone.
f) Risolvi i conflitti: tieni l’hero section E il paragrafo introduttivo, combinandoli nell’ordine corretto. Combina anche gli stili CSS.
g) Completa il merge. Verifica che index.html e style.css siano corretti. Pulisci i branch.

❓ 4. Quiz di verifica

1. Quando nasce un conflitto di merge?

Mostra risposta

Un conflitto nasce quando due branch hanno modificato la stessa riga dello stesso file in modo diverso. Git non può decidere autonomamente quale versione tenere, quindi si ferma e chiede a te di risolvere. Se i due branch hanno modificato file diversi, o righe diverse dello stesso file, Git combina automaticamente le modifiche senza conflitto.


2. Cosa significano i marker <<<<<<< HEAD, ======= e >>>>>>> feature/x?

Mostra risposta

Delimitano le due versioni in competizione. Tra <<<<<<< HEAD e ======= c’è la versione del branch corrente (HEAD, di solito main). Tra ======= e >>>>>>> feature/x c’è la versione del branch che stai integrando. Devi scegliere quale tenere (o combinarle), poi rimuovere tutti i marker. I marker non sono codice — sono solo indicatori messi da Git. Se li lasci nel file, il codice sarà rotto.


3. Dopo aver risolto il conflitto modificando il file, quale sequenza di comandi completa il merge?

Mostra risposta

git add file-risolto seguito da git commit. Il git add dice a Git “ho risolto il conflitto in questo file — la versione attuale è quella corretta”. Il git commit completa il merge creando il merge commit. Se non specifichi -m, Git apre l’editor con un messaggio predefinito (“Merge branch ‘…'”) che puoi personalizzare. Se ci sono più file in conflitto, devi fare git add per ciascuno prima del commit.


4. Il merge è troppo complicato e vuoi annullarlo. Come fai?

Mostra risposta

git merge --abort. Questo annulla il merge in corso e riporta il repository allo stato precedente — come se non avessi mai eseguito git merge. I file tornano alla versione di main prima del merge, senza marker di conflitto. Puoi usare --abort in qualsiasi momento durante la risoluzione dei conflitti, anche se hai già modificato alcuni file. Tutto viene ripristinato. Puoi poi riprovare il merge quando sei pronto.


5. Cosa succede se fai git commit senza rimuovere i marker <<< dal file?

Mostra risposta

Git committa il file così com’è, marker inclusi. Git non verifica che i marker siano stati rimossi — si fida di te. Il risultato è un file con caratteri come <<<<<<< e ======= nel mezzo del codice, che quasi certamente romperà l’applicazione. Ecco perché è importante sempre verificare il contenuto del file dopo aver risolto un conflitto, e prima di fare git add e git commit.


6. In VS Code, cosa fa il pulsante “Accept Both Changes”?

Mostra risposta

Mette entrambe le versioni nel file, una dopo l’altra: prima la versione di HEAD, poi quella del branch in arrivo. I marker vengono rimossi automaticamente. Attenzione: il risultato potrebbe non avere senso — ad esempio se entrambe le versioni definiscono la stessa proprietà CSS, avrai una proprietà duplicata. Dopo aver cliccato “Accept Both”, verifica sempre che il codice risultante sia logicamente corretto. Se non lo è, modifica manualmente il risultato.


📚 Riepilogo della lezione

Cosa hai imparato oggi:
  • Un conflitto nasce quando due branch modificano la stessa riga dello stesso file
  • Git si ferma in stato (main|MERGING) e segna i conflitti con marker nel file
  • Marker: <<<<<<< HEAD (tua versione) · ======= (separatore) · >>>>>>> feature (versione dell’altro)
  • 4 scelte: tieni HEAD · tieni feature · tieni entrambi · riscrivi
  • Procedura: status → apri → scegli → rimuovi marker → add → commit
  • git merge --abort — annulla tutto e torna allo stato precedente
  • Mai lasciare i marker nel file — Git non li controlla, committa tutto
  • VS Code evidenzia i conflitti con colori e pulsanti — comodo ma verifica sempre il risultato

Prossima lezione →

Lezione 11 — Rebase
Differenza concettuale merge vs rebase. git rebase interattivo.
La regola d’oro: mai rebase su branch pubblici.

Corso Git & GitHub · Per studenti e insegnanti · Lezione 10 di 45