Come risolvere i conflitti di fusione in Git: Tutorial

Che cos’è un conflitto di merge Git?

Il sistema di controllo versione Git è tutto incentrato sul lavorare in team e contribuire ai progetti. Gli sviluppatori di solito lavorano su rami isolati e, quando hanno completato il lavoro, fondono le modifiche nel ramo principale. Questo tipo di lavoro di squadra è altamente produttivo ed efficace per la ricerca di bug. A volte, più sviluppatori lavorano sulla stessa riga di codice e quando cercano di fondere le modifiche, si verificano conflitti.


Esempio semplice di conflitto Git

L’immagine soprastante offre un perfetto esempio di come si verifichi una tipica conflittualità di merge in Git. Il ramo principale contiene un file con il testo “HELLO, WORLD!”. Il utente abid flette il ramo principale e modifica il testo in “HELLO, CAT!”. Mentre abid sta facendo le sue modifiche, il ramo principale originale viene anche modificato in “HELLO, DOG!”. Il merge di questi rami genera una conflittualità di merge e blocca il processo.

Il comando `git merge` ha come compito primario combinare due rami e risolvere automaticamente i conflitti. Tuttavia, a volte si verificano conflitti in cui due persone hanno modificato la stessa riga di codice o hanno rimosso file cruciali su cui un altro sviluppatore stava lavorando. Git marcherà queste modifiche e bloccherà il processo di merge. In questo caso, il conflitto non è stato risolto automaticamente, ma il developer deve fare modifiche manualmente o usare strumenti per risolvere il conflitto.

Tipi di merge

Git merge e rebase sono due modi per integrare i commit dal ramo obiettivo al ramo di origine. Inoltre, Git merge esegue un merge fast-forward o un merge no-fast-forward. Se l’estremità del ramo obiettivo esiste nel ramo di origine, allora per default il tipo di merge sarà un merge fast-forward, e se manca, allora sarà un merge no-fast-forward. Git rebase è un altro tipo di merge che resequenza la storia dei commit del ramo obiettivo.

Merge fast-forward

By default, Git merge utilizza il fast-forward per integrare i commit mancanti nella branchedi destinazione. Per esempio, viene usato per aggiornare la branchedi lavoro locale da un server remoto usando il comando pull. Il fast-forward non solleva problemi di conflitto di merge poiché Git non lo applicherà se la testa della branchedi destinazione manca nella branchedi origine. 

Merge senza fast-forward

Un merge senza fast-forward è anche chiamato merge a tre vie o vero merge. Crea un nuovo commit nella branchedi destinazione integrando le modifiche sia nella branchedi origine che in quella di destinazione. Le modifiche vengono unite dopo l’ultimo commit comune in entrambe le branche. Nel nostro caso, è dopo il commit C. Questo tipo di merge solleverà un conflitto di merge Git se la branchedi origine è in disputa con quella di destinazione. Nel diagramma sopra, il commit di merge (X) viene creato integrando la branchedi origine e quella di destinazione, dove K ed E sono i genitori del commit di merge. 

Rebase 

Git rebase è un po’ diverso dagli altri tipi. Modifica la sequenza della storia dei commit della branchedi destinazione. Il rebase integra la branchedi origine in modo che la branchedi destinazione contenga tutte le modifiche dalla branchedi origine, seguite da tutti i commit della branchedi destinazione dopo l’ultimo commit comune. Nel nostro caso, l’ultimo commit comune è C, mentre D ed E provengono dalla branchedi origine. Il commit K* è lo stesso di K con un diverso id di commit. Invece di collegare C, sarà collegato a E. Come per un merge senza fast-forward, se ci sono problemi di compatibilità nella branchedi origine e in quella di destinazione, Git solleverà un problema per risolvere il conflitto prima di concludere il rebase. 

Tipi di conflitti di merge Git

Ci sono due tipi di conflitti di unione in Git: all’inizio e durante il processo di unione – Atlassian. In questa sezione, impareremo entrambe le tipologie e le modalità per risolvere ciascun scenario. 

All’Inizio Dell’Unione

L’unione di Git fallirà all’inizio se ci sono modifiche nel directory di lavoro o nella zona di staging. Fallisce all’inizio per impedire che le modifiche vengano sovrascritte dai commit di unione in entrata. Questo avviene a causa di conflitti con i cambiamenti locali, non con altre branch o sviluppatori. Per stabilizzare lo stato locale, è possibile utilizzare comandi come git stash, git commit, git checkout, o git reset.

Durante L’Unione

Un fallimento durante l’unione significa che c’è un conflitto tra la branch di origine e la branch di destinazione dove molti sviluppatori hanno modificato lo stesso file. Se l’unione automatica fallisce, Git chiederà di risolvere i problemi manualmente. È anche possibile utilizzare strumenti terzi per assistere nella visualizzazione e nell’integrazione dei cambiamenti.

Comandi Per Risolvere I Conflitti Di Unione Di Git

In questa sezione, impareremo diversi comandi nativi per visualizzare e risolvere i conflitti di unione di Git. 

Comandi Comuni

Git status è il comando più utilizzato per mostrare lo stato dei file modificati, dell’area di staging e dei commit. Durante il processo di unione, è utilizzato per identificare i file in conflitto.

git status

Il log di Git con gli argomenti –merge produce la lista dei commit che sono in conflitto con il ramo di origine.

git log --merge

Per default, l’opzione git diff mostrerà le differenze tra i cambiamenti non commessi e i precedenti commit. Git diff viene utilizzato per confrontare branch, commit e file. È utile per prevenire futuri conflitti di merge.

git diff

Comandi per il fallimento del merge all’inizio

Il comando checkout viene usato per annullare cambiamenti o passare a un nuovo o vecchio branch.

git checkout

Il reset di Git serve per reindirizzare i cambiamenti nel directory di lavoro e nell’area di staging.

git reset --mixed

Comandi per i conflitti durante il merge

L’argomento –abort interromperà il processo di merge e riporterà i cambiamenti al loro stato originale prima dell’inizio del merge.

git merge --abort

Il reset di Git viene generalmente utilizzato durante il processo di merge per reindirizzare i file in conflitto al loro stato originale.

git reset

Risolvere i conflitti tra file eliminati e modificati

Si verificherà un conflitto di Git se hai eliminato il file nel branch corrente, e qualcun altro l’ha modificato in un altro branch. In questo caso, puoi aggiungere un file e committare,

git add <filename>

o puoi rimuovere il file e committare.

git rm <filename>

Strumenti di visualizzazione del merge

Gli strumenti di merge sono strumenti visivi intuitivi per identificare e risolvere tutti i tipi di conflitti di merge. Alcuni strumenti supportano capacità aggiuntive come il confronto delle modifiche, le operazioni Git e la gestione di progetti e repository. Esistono due tipi di strumenti di merge Git: solo terminale e basati su GUI. Gli strumenti basati su terminale si aprono in PowerShell o Bash, mentre quelli basati su GUI si aprono in un ambiente finestrato.

Per controllare l’elenco degli strumenti installati e validi, usa:

git mergetool --tool-help

L’elenco consiste in tutti gli strumenti validi che possono essere installati e integrati con i comandi git.

Ad esempio, abbiamo vim e nvim installati di default, e se vuoi vedere la differenza tra un file non confermato e un commit precedente, digita:

git difftool --tool=vimdiff3

Lo strumento vimdiff3 evidenzia le modifiche e ti permette di confrontare i commit all’interno del terminale.

Differenza tra Due Versioni dello Stesso File in Vimdiff3

Meld

Meld è uno strumento gratuito e open-source che porta la risoluzione dei conflitti di merge a un altro livello. Per integrarlo con Git, devi prima scaricare e installare il setup dal sito ufficiale. Successivamente, aggiungilo alla configurazione globale in modo che, per impostazione predefinita, Git avvii Meld per risolvere i conflitti.

I comandi di configurazione qui sotto sono applicabili solo agli utenti Windows. L’unico cambiamento che devi fare è cambiare il percorso del file installato di Meld per Mac o Linux.

git config --global merge.tool meld git config --global mergetool.meld.path "C:/Program Files (x86)/Meld/Meld.exe" git config --global diff.tool meld git config --global difftool.meld.path "C:/Program Files (x86)/Meld/Meld.exe"

Dopo aver impostato i valori predefiniti, puoi digitare git difftool all’interno della directory locale di Git per lanciare la versione Windows di Meld, o puoi usare git mergetool per risolvere i conflitti di fusione come mostrato qui sotto.

Risoluzione di un Conflitto di Fusione con Meld

VSCode

VSCode fornisce il modo migliore e più popolare per risolvere i conflitti di fusione. Quando Git non riesce a fondere i file automaticamente, VSCode evidenzierà il codice in conflitto e ti darà quattro opzioni: accettare le modifiche correnti, accettare le modifiche in entrata, accettare entrambe le modifiche o confrontare le modifiche. Puoi usare queste opzioni per pulire il tuo file e risolvere tutti i problemi in sospeso.

Risoluzione di un Conflitto di Fusione con VSCode

Se stai cercando una soluzione completa per le tue operazioni Git, prova GitKraken. Include un client gratuito, l’estensione VSCode e fornisce una tool integrata per risolvere i conflitti di fusione.

Come risolvere un conflitto di fusione di Git

In questa sezione, impareremo a creare un conflitto di merge in Git e poi a risolverlo. Il tutorial è diviso in due parti. Nella prima parte, impareremo a risolvere i conflitti di Git localmente; la seconda parte riguarda la risoluzione dei conflitti con un server remoto (GitHub).

Conflitto di Merge Locale

Creare conflitti di merge ci aiuterà a capire come sorgono questi problemi in primo luogo. Potremo poi utilizzare modi creativi per risolverli o addirittura prevenirli in futuro. 

Ora creeremo un repository Git con un solo file e creeremo il nostro primo commit per iniziare. 

  1. Crea una cartella chiamata datacamp.
  2. Cambia la directory in datacamp.
  3. Inizializza Git.
  4. Crea un file README.md con il titolo dato.
  5. Stadia e committa le modifiche in un file.
mkdir datacamp cd datacamp git init echo "# How to Resolve Git Merge Conflict" > README.md git add README.md git commit -m "first commit" >>> [main (root-commit) 8199ea2] first commit >>> 1 file changed, 1 insertion(+) >>> create mode 100644 README.md

Successivamente, creeremo un nuovo branch readme e cambieremo il titolo da “..Git Merge..” a “..Git..”. Aggiungi il file e crea il commit usando l’argomento -am.

git checkout -b readme echo "# How to Resolve Git Conflict" > README.md git commit -am "new branch conflict added" >>> [readme 155f694] new branch conflict added >>> 1 file changed, 1 insertion(+), 1 deletion(-)

Torna al branch principale e aggiungi una nuova riga al file README.md usando >>. Salvando le modifiche e creando i commit, abbiamo creato con successo un conflitto tra due versioni dello stesso file. 

git checkout main echo "New change in base branch" >> README.md git commit -am " a line added to base branch Readme file" >>> [main f1f1874] a line added to base branch Readme file >>> 1 file changed, 1 insertion(+)

Come possiamo vedere, durante la fusione del branch readme, Git ha mostrato un messaggio dicendo che la fusione automatica è fallita e dobbiamo apportare modifiche manualmente e poi committare il risultato.

git merge readme >>> Auto-merging README.md >>> CONFLICT (content): Merge conflict in README.md >>> Automatic merge failed; fix conflicts and then commit the result.

Segue la traduzione in italiano:

Risolveremo il problema manualmente aprendo e modificando il file nel Notepad. L’immagine sottostante mostra una freccia con HEAD, un divisore e una freccia con una direzione diversa con un readme. La parte HEAD mostra le modifiche esistenti nel ramo principale, e la parte readme è il ramo che vogliamo unire, che consiste in un’intestazione diversa.

Risolvere un Conflitto di Merge Manualmente

Per risolvere il problema, rimuoveremo la parte del ramo readme, le frecce e il divisore. La versione finale del file dovrebbe apparire pulita, come mostrato di seguito.

Conflitto Risolto

Dopo aver aggiunto il file e creato un commit, il conflitto di merge sarà risolto. È il metodo più comune e più semplice per risolvere i problemi. È anche possibile utilizzare un ambiente di sviluppo integrato (IDE) per risolvere i problemi più rapidamente.

git commit -am "conflict resolved in file README.md" >>> [main 9994a29] conflict resolved in file README.md

Conflitto di Merge Remoto

Per creare e risolvere conflitti di merge remoti, dobbiamo creare un nuovo repository su GitHub.

Creare un Nuovo Repository su GitHub

Successivamente, aggiungere il nome remoto (origine) con l’indirizzo al repository e spingere tutti i cambiamenti dal repository locale alla branch principale remota utilizzando l’upstream.

git remote add origin https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git git push --set-upstream origin main >>> Enumerating objects: 12, done. >>> Counting objects: 100% (12/12), done. >>> Delta compression using up to 4 threads >>> Compressing objects: 100% (6/6), done. >>> Writing objects: 100% (12/12), 998 bytes | 499.00 KiB/s, done. >>> Total 12 (delta 2), reused 0 (delta 0), pack-reused 0 >>> remote: Resolving deltas: 100% (2/2), done. >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> * [new branch] main -> main >>> branch 'main' set up to track 'origin/main'.

Per creare un conflitto, bisogna apportare modifiche ai file README.md locali e remoti. È possibile utilizzare l’editor di file di GitHub per cambiare “..Git merge..” in “..Sit-Merge..” e quindi committare le modifiche.

Modifiche con l’editor di GitHub

Successivamente, nel repository locale, modificare il file README.md aggiungendo solo un semplice titolo e committare le modifiche.

echo "# How to Resolve Merge Conflicts in Git Tutorial" > README.md git commit -am "local branch changes in README.md" >>> [main c677a13] local branch changes in README.md >>> 1 file changed, 1 insertion(+), 4 deletions(-)

Infine, spingere i cambiamenti sul server remoto. Notare che Git ha sollevato l’errore con indicazioni su come eliminare il problema.

git push >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> ! [rejected] main -> main (fetch first) >>> error: failed to push some refs to 'https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git' >>> hint: Updates were rejected because the remote contains work that you do >>> hint: not have locally. This is usually caused by another repository pushing >>> hint: to the same ref. You may want to first integrate the remote changes >>> hint: (e.g., 'git pull ...') before pushing again. >>> hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Seguiremo l’indicazione più semplice, ossia prelevare il file dal server remoto prima di spingere.

Il prelievo del file è fallito a causa di un conflitto di fusione nel file README.md. Potremmo correggerlo manualmente utilizzando Notepad, ma questa volta userremo una tool visiva per assistereci in questo processo.

git pull >>> remote: Enumerating objects: 5, done. >>> remote: Counting objects: 100% (5/5), done. >>> remote: Compressing objects: 100% (2/2), done. >>> remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 >>> Unpacking objects: 100% (3/3), 681 bytes | 75.00 KiB/s, done. >>> From https://github.com/kingabzpro/DataCamp-Git-Merge-Guide >>> aaf149d..49b7d14 main -> origin/main >>> Auto-merging README.md >>> CONFLICT (content): Merge conflict in README.md >>> Automatic merge failed; fix conflicts and then commit the result.

La tool di fusione Meld identificherà i file in conflitto e li mostrerà nell’applicazione grafica di Meld.

git mergetool >>> Merging: >>> README.md >>> Normal merge conflict for 'README.md': >>> {local}: modified file >>> {remote}: modified file

Ci sono tre colonne: README_LOCAL_473.md, README.md e README_LOCAL_473.md. Se pensate che le modifiche remote siano valide, cliccate sulla freccia nera nella colonna remota; e se volete che le modifiche locali persistano, cliccate sulla freccia nera nella colonna locale. E ‘così semplice.

Conflitto Risolto Utilizzando lo Strumento di Merge Meld

Dopo aver apportato modifiche, salva il file e effettua il commit. Come puoi vedere, caricare un file su un server remoto non genera un errore di conflitto di merge.

git commit -am "remote main branch conflict resolved" git push >>> Enumerating objects: 16, done. >>> Counting objects: 100% (16/16), done. >>> Delta compression using up to 4 threads >>> Compressing objects: 100% (6/6), done. >>> Writing objects: 100% (10/10), 1.08 KiB | 550.00 KiB/s, done. >>> Total 10 (delta 2), reused 0 (delta 0), pack-reused 0 >>> remote: Resolving deltas: 100% (2/2), completed with 1 local object. >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> 49b7d14..8f5c3aa main -> main

Abbiamo risolto con successo sia i conflitti di merge locali che remoti. Questi conflitti vengono affrontati quotidianamente da data scientist e ingegneri del machine learning. Per migliorare le tue competenze nelle operazioni Git, segui un corso di Introduzione a Git.

Conclusione

Risolvere i conflitti di merge in Git è un compito complesso e altamente rischioso, poiché potresti danneggiare il software unendo codice difettoso. Gli strumenti di merge forniscono un ambiente user-friendly con un modo più sicuro per rilevare e risolvere i conflitti di merge. In questo tutorial, abbiamo imparato perché si verificano i conflitti in Git e come risolverli. Abbiamo anche trattato vari tipi di merge e conflitti, comandi Git utili e strumenti visuali. Nella sezione finale, abbiamo creato un conflitto di merge e lo abbiamo risolto in un repository locale e remoto.

Se sei nuovo a Git e vuoi imparare come funziona, leggi: Introduzione a Git e GitHub Tutorial.

Source:
https://www.datacamp.com/tutorial/how-to-resolve-merge-conflicts-in-git-tutorial