Git merge conflict: come risolvere manualmente

di Lorenzo Neri
412 visualizzazioni

Ti stai chiedendo come risolvere manualmente un git merge conflict? Seguimi in questo articolo per scoprire i passi giusti da fare!

Una delle grane più pesanti da risolvere nello sviluppo software collaborativo, sono proprio i conflitti causati dal famigerato comando “git merge”. Diversi strumenti permettono anche di automatizzare la risoluzione dei conflitti, quindi i conflict merge, ma posso essere sincero?

Non è mai una buona idea affidarsi a questi strumenti.

Ciao mi chiamo Lorenzo Neri e sono un informatico: realizzo contenuti per aiutare le persone a padroneggiare l’arte del nuovo millennio, ovvero l’informatica!

La strada da percorrere per risolvere manualmente un conflict causato da un merge lo ammetto: è più onerosa in termini di tempo!

Ma è anche la più sicura e la più pratica, ciò detto, partiamo da uno scenario chiaro e semplice per capire come fare.

Situazione di partenza: due branch

Non c’è conflitto da merge senza due branche da “mergiare” 😀

Come ti dicevo prima, partirò da un esempio molto semplice: hai un repository con due branch allineati allo stesso punto della storia.

Vedi che i branch “feature” e “master” sono allo stesso punto: HEAD sta puntando a master.

Il prossimo step, è l’aggiunta di un file che chiameremo “file_main.py” e scriviamo al suo interno le seguenti righe:

from datetime import datetime
print("ciao, stampo il momento esatto a video")
adesso = datetime.now()
print(adesso)

Niente di trascendentale, te lo dicevo!

A questo punto, lo aggiungiamo alla storia del nostro repository e registriamo il cambiamento con un commit:

git add file_main.py
git commit -m "prima versione di file_main.py"

Ora, vuoi aggiungere una variazione all’interno del file, una feature!

Perciò, ti sposti sul branch “feature”:

git checkout feature

Siccome anche il branch “feature” era allineato al branch “master”, il file “file_main.py” è sempre con noi con lo stesso contenuto.

A seguito del contenuto già presente, aggiungiamo altro codice, fino ad arrivare a questo punto:

from datetime import datetime
print("ciao, stampo il momento esatto a video")
adesso = datetime.now()
print(adesso)
print("e stampo anche un secondo momento")
ancora_piu_adesso = datetime.now()
print(ancora_piu_adesso)

Fatto ciò registriamo un commit sul branch “feature”:

git add file_main.py
git commit -m "ho modificato file_main.py"

E ci troviamo a questo punto della storia:

Il tuo obiettivo ricordartelo: è quello di eseguire un merge di queste ultime modifiche all’interno del branch “master”.

E come facciamo? Beh semplice:

git checkout master
git merge feature

E qui, comincia la samba! Congratulazioni: hai dato vita al tuo git merge conflict!

Come risolvere manualmente il conflict

Ebbene, dopo aver eseguito il merge, ti sarai trovato sicuramente un messaggio di questo tipo:

Auto-merging file_main.py
CONFLICT (add/add): Merge conflict in file_main.py
Automatic merge failed; fix conflicts and then commit the result.

In un altro articolo ho parlato di come risolvere i conflitti e se vuoi approfondire lo trovi qui, ma ora dobbiamo concentrarci sulla risoluzione manuale.

Il messaggio è abbastanza chiaro: git non è stato in grado di effettuare il merge automatico e ti dice anche su quali file non è stato in grado di eseguirlo!

… Nel nostro caso siamo fortunati e c’è solamente “file_main.py” e vediamo un po’ cosa c’è al suo interno:

from datetime import datetime
print("ciao, stampo il momento esatto a video")
adesso = datetime.now()
print(adesso)
<<<<<<< HEAD
=======
print("e stampo anche un secondo momento")
ancora_piu_adesso = datetime.now()
print(ancora_piu_adesso)
>>>>>>> feature

La potenzialità di git è la semplicità: esso opera sulla differenza di righe e ne ho parlato proprio qui!

E di fatto, il merge conflict si basa proprio sulla differenza di righe.

Avrai sicuramente notato le righe “<<<<<<< HEAD” e “=======” per non parlare della terza “>>>>>>> feature e non sono posizionate in quei punti precisi per puro caso: no.

Se prendi il codice che abbiamo scritto assieme e “committato” in “master” noterai che termina proprio dove c’è la riga “======” a differenza del codice che sta dopo di essa e termina con “>>>>>> feature” che guardacaso è proprio quello che abbiamo scritto e “committato” nel branch “feature”.

Rispettivamente, le prime due righe segnano come una linea di confine tutte le righe presenti nel branch a cui lo HEAD sta attualmente puntando, nel nostro caso “master”.

La riga con gli uguali e tutto ciò che segue fino a “>>>>>> feature”, si tratta delle righe che sono state registrate nel commit del branch con cui vogliamo eseguire il merge.

Nel nostro caso, si tratta del branch “feature”.

Git merge conflict: come risolvere manualmente abbracciando tutto

Partiamo dal caso più semplice: vuoi “abbracciare” tutti i cambiamenti, quindi includere ciò che hai aggiunto nel branch “feature”.

Cosa dovrai fare? Banalmente cancellare le tre linee che rappresentano il confine fra i due commit e quindi:

from datetime import datetime
print("ciao, stampo il momento esatto a video")
adesso = datetime.now()
print(adesso)
print("e stampo anche un secondo momento")
ancora_piu_adesso = datetime.now()
print(ancora_piu_adesso)

E registrare le modifiche, così:

git add file_main.py
git commit

A questo punto, ti apparirà in console il seguente testo:

Merge branch 'feature'
# Conflicts:
#       file_main.py
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#       .git/MERGE_HEAD
# and try again.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#       modified:   file_main.py

Puoi aggiungere del testo a tuo piacimento ed esso diverrà il messaggio di merge registrato nella storia del tuo repository.

Una volta salvato il messaggio avrai risolto il merge unendo le modifiche dei due branch!

E ti troverai in questa situazione:

Ma prima di salutarti, vediamo come fare negli altri casi.

Come risolvere manualmente “a metà”

Può capitare anche che non vuoi affatto accettare tutte le modifiche, ma scartarne alcune.

Per esempio, vuoi eliminare ciò che è stato aggiunto nel branch “feature” e quindi il ramo da cui stai prendendo le modifiche?

Ti basta fare questo:

from datetime import datetime
print("ciao, stampo il momento esatto a video")
adesso = datetime.now()
print(adesso)

Per poi eseguire i comandi git che abbiamo visto prima.

Oppure, vuoi mantenere solo le modifiche provenienti dal ramo che stai “mergiando”:

print("e stampo anche un secondo momento")
ancora_piu_adesso = datetime.now()
print(ancora_piu_adesso)

Anche qui, dovrai eseguire i comandi visti prima per finalizzare il merge.

Se ci fai caso, anzi se fai caso a questi due casi, mi sono basato sulle tre linee di demarcazione di cui ti parlavo prima.

Continua a scoprire di più con questi articoli!

Lascia un commento

Questo sito potrebbe fare uso di cookie e siccome l'UE mi obbliga a fartelo presente, eccoti il classico banner dove puoi decidere come gestirli. Accetta Leggi di più