Che cosa sono i conflitti di merge?

Completato

In questo articolo viene illustrato come 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 era basato sul ramo stesso. È piuttosto basato sul commit HEAD di main eseguito al momento. Non è a conoscenza di tutti i commit applicati a main da allora. I commit attualmente tracciati non verranno necessariamente piegati nello stato corrente del ramo senza sovrascrivere le modifiche recenti.

Se si scopre che i feature-branch commit non si sovrappongono ai commit paralleli eseguiti a main dopo la creazione del ramo, non ci sono 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.

A pull request with no merge conflicts.

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.

A merge conflict.

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. Invece, li punta alla persona che tenta di unire in modo che possano risolverli nel ramo di confronto prima di tentare di eseguire di nuovo il merge.

A pull request with merge conflicts.

Risoluzione di conflitti di merge

Per risolvere i conflitti di unione, GitHub genera un file ibrido temporaneo che include le differenze di 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 (=======).

Resolving a merge conflict.

È possibile usare questa visualizzazione per modificare direttamente il file se le modifiche sono minime. Se si decide di mantenerlo, viene eseguito il commit del risultato finale nel ramo di confronto. In alternativa, se l'unione è più interessata, è preferibile usarla 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, rimangono nel file e non vengono commentati.

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 l'unione.

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 esegue il git pull pull di tutti i commit del ramo di base non ancora applicati 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 esegue l'unione di tutte le modifiche apportate dopo la creazione del ramo (o dell'ultimo pull).

Il pull degli aggiornamenti nel ramo potrebbe comportare conflitti di merge, ma è corretto. Li otterresti comunque in un secondo momento, e ottenendoli in precedenza, spesso sono più facili da affrontare.

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, potrebbero essere presenti modifiche di definizione della classe in altri file che causano la mancata compilazione del codice. Questa modifica non provocherebbe un conflitto di merge quando si esegue il merge in un secondo momento, ma interromperebbe la compilazione se non è stata testata per prima. È 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 da comportarsi come se fosse solo diramato dallo stato corrente del ramo di base. Questa rebase indica che tutte le modifiche vengono confrontate con lo stato più recente del ramo di base e non il commit originale da cui è stato originariamente creato il ramo. La ribasatura può rendere più semplice tenere traccia della cronologia dopo l'eventuale unione, perché i commit seguiranno 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.