Che cosa sono i conflitti di merge?
In questo articolo viene descritto in che modo la risoluzione dei conflitti di merge consente agli sviluppatori di produrre il risultato migliore da due origini sovrapposte.
Flusso di GitHub
Oltre a fornire una piattaforma per lo sviluppo di software basato sulla collaborazione, GitHub offre anche un flusso di lavoro prestabilito, progettato per ottimizzare l'uso delle diverse funzionalità. Mentre questa unità tratta nello specifico i conflitti di merge, è consigliabile leggere prima di tutto Understanding the GitHub flow (Informazioni sul flusso di GitHub).
Merge di rami
Si consideri uno scenario in cui uno sviluppatore crea un ramo denominato feature-branch
basato su main
e crea due commit. Durante questa attività, qualcun altro esegue il merge di una richiesta pull non correlata in main
. Che cosa succede quando lo sviluppatore tenta il merge di feature-branch
di nuovo in main
?
Risposta: dipende.
Anche se feature-branch
è stato creato da main
, non è basato sul ramo stesso. È piuttosto basato sul commit HEAD di main
eseguito al momento. Non riconosce tutti i commit applicati a main
da allora. I commit che sta attualmente verificando non raggiungono necessariamente lo stato corrente del ramo senza sovrascrivere le modifiche recenti.
Se avviene i commit di feature-branch
non si sovrappongono a commit paralleli eseguiti in main
dopo la creazione del ramo, non si verificano problemi. I nuovi file possono essere aggiunti. I file non modificati possono essere eliminati. Le righe di codice modificate in main
possono essere modificate in feature-branch
, purché le attività parallele non siano cambiate dopo la creazione di feature-branch
.
Che cosa succede invece se entrambi i set di commit includono modifiche alla stessa riga di codice? Questo tentativo di merge non riuscirà a causa di un conflitto di merge.
Che cosa sono i conflitti di merge?
I conflitti di merge vengono generati quando uno sviluppatore tenta il merge di modifiche che sovrascriverebbero inavvertitamente altre modifiche parallele. Non è importante il modo in cui è stato eseguito il merge delle altre modifiche nel ramo di base. Git non sovrascrive automaticamente un set di modifiche a favore di un altro. Informa invece la persona che sta tentando di eseguire il merge, in modo che possa risolverle nel ramo di confronto prima di ritentare il merge.
Risoluzione di conflitti di merge
Per semplificare la risoluzione di conflitti di merge, GitHub genera un file ibrido temporaneo che include le differenze da ogni ramo. Per convenzione, il testo del ramo di confronto viene visualizzato sopra il ramo di base, separato da una riga di segni di uguale (=======
).
È possibile usare questa visualizzazione per modificare direttamente il file se le modifiche sono minime. Se si decide di confermare il risultato finale, ne viene eseguito il commit nel ramo di confronto. In alternativa, se il merge è più interessato dalle modifiche, può essere preferibile intervenire usando altri strumenti di sviluppo. In entrambi i casi, non dimenticare di rimuovere tutti i marcatori di ramo dal codice prima di eseguire il commit. Se si dimentica di rimuovere questi marcatori quando si esegue il commit della risoluzione dei conflitti, questi restano nel file e non vengono impostati come commento.
Nota
Questa unità descrive la risoluzione dei conflitti di merge nel contesto di un browser. Sono disponibili anche molte piattaforme di sviluppo, come Visual Studio, che offrono esperienze di risoluzione dei conflitti di merge integrate.
Dopo aver risolto tutti i conflitti di merge nel ramo, è possibile ritentare il merge.
Evitare conflitti di merge
Alcuni conflitti di merge sono inevitabili. Qualsiasi merge può produrre conflitti di merge per altre richieste pull in attesa di approvazione. Tuttavia, un modo efficace per ridurre la complessità dei conflitti di merge consiste nell'eseguire il pull frequente del ramo.
Pull tempestivi e frequenti
Il comando git pull
esegue il pull di qualsiasi commit del ramo di base non ancora applicato al ramo corrente. È concettualmente simile al comando per il recupero della versione più recente usato da molti sistemi di controllo della versione per consentire l'aggiornamento del codice locale alla versione più recente. Quando si esegue il pull degli aggiornamenti per il ramo, si uniscono tutte le modifiche apportate dalla creazione del ramo (o dall'ultimo pull).
Il pull degli aggiornamenti nel ramo può produrre conflitti di merge, ma questo è accettabile. Si sarebbero prodotti comunque in seguito e, se avvengono inizialmente, sono in genere più facili da risolvere.
Oltre a ridurre l'impatto dei conflitti di merge, il pull degli aggiornamenti consente anche di integrare le modifiche di cui è stato eseguito il commit nel ramo mentre si lavora. In questo modo, è possibile affrontare i possibili problemi più tempestivamente. Ad esempio, alcune modifiche alla definizione di classe in altri file possono impedire la compilazione del codice. Questa modifica non provocherebbe un conflitto di merge al successivo merge, ma interromperebbe la compilazione se non testata in anticipo. È consigliabile eseguire spesso il pull degli aggiornamenti per mantenere il ramo il più vicino possibile alla base.
Riordino della cronologia con git rebase
Il comando git rebase
(o git pull --rebase
) riscrive la cronologia del ramo per usare il commit HEAD corrente del ramo di base come base. In altre parole, il ramo viene aggiornato in modo che si comporti come se fosse stato creato solo dallo stato corrente del ramo di base. Con questa riassegnazione, tutte le modifiche vengono confrontate con lo stato più recente del ramo di base e non con il commit originale da cui è stato creato il ramo in origine. La riassegnazione può rendere molto più facile tenere traccia della cronologia dopo il merge finale, perché i commit seguono i commit paralleli precedenti in modo lineare. È consigliabile riassegnare il ramo immediatamente prima del merge upstream.
Altre informazioni sulla riassegnazione in Git e sulla risoluzione dei conflitti di merge dopo una riassegnazione in Git.