Ciao a tutti,
oggi andiamo al succo di GIT parlando del branch. Ricordate il primo tutorial? Avevamo detto che GIT serve per lavorare in team su uno stesso progetto. Questo tool infatti permette di creare delle così dette branch ovvero delle diramazioni del progetto. Pensiamo per semplicità (ma senza perdita di generalità) di avere un team di due persone. Quando si lavora su un progetto è sempre buona cosa spartirsi i lavori per operare in parallelo quindi diciamo che A lavora sulla Feature 1 mentre B sulla Feature 2.
Idealmente quindi vengono fatte due branch, una per la Feature 1 e l'altra per la Feature 2.
BRANCH
Mettiamoci nei panni di A. Siamo nel nostro terminale, dentro la cartella del progetto e per creare una nuova branch eseguiamo il seguente comando
git branch feature1
In questo momento quindi abbiamo un nuova branch dal nome feature1 con il progetto uguale alla branch master (quella principale).
Da quando la branch viene creata, A lavorerà solo sulla branch feature1. Successivamente vedremo come gestire master che rappresenta la branch nella quale c'è la versione stabile del progetto.
Per eliminare la branch locale invece basta eseguire
git branch -d feature1
Per passare alla nuova branch eseguiamo un comando che avevamo visto anche nel tutorial precedente: checkout.
git checkout feature1
Ora siamo sulla branch feature1. Tutte le modifiche e i relativi commit che facciamo qui, non condizioneranno la branch master.
Per prova modifichiamo in qualsiasi modo il nostro file index.html. Procedendo con l'add e il commit, scattiamo l'istantanea al progetto nella branch attuale: la branch featre1. Se adesso passiamo alla branch master con il comando
git checkout master
noteremo che il file modificato nella branch feature1 sarà sempre quello della versione precedente.
Ripassiamo alla branch feature1
git checkout feature1
Abbiamo fatto la nostra modifica e adesso vogliamo pushare su github. Ancora però su github non c'è la branch feature1 quindi la si crea lanciando questo comando
git push --set-upstream origin feature1
Adesso, facendo push possiamo aggiornare la branch online.
MERGE
Dal punto di vista pratico, normalmente, la branch nasce per l'implementazione di una feature e muore con la sua finalizzazione. Schematicamente la possiamo rappresentare in questo modo
Dalla branch master si fa la branch feature1, A lavora sulla feature (i nodi rappresentano i vari commit) e, una volta che ha terminato, viene fatta la così detta operazione di merge ovvero l'operazione con la quale il codice sviluppato nella branch feature1 viene integrato nella master branch.
Diciamo che A abbia finito di implementare la Feature 1, ha fatto il commit, l'ha testata e funziona tutto. Visto che funziona tutto, può aggiornare la master branch. Per fare questo basterà spostarsi sulla master branch, e dare il comando di merge come di seguito:
git checkout master git merge feature1
Notiamo quindi che per fare un merge bisogna essere sulla branch di destinazione e nel comando git merge va indicata la branch sorgente.
Lavorare in Team e risolvere i conflitti
A e B stanno sulle proprie branch a lavorare rispettivamente sulla Feature 1 e sulla Feature 2. B finisce per primo e fa quello che abbiamo visto nel paragrafo precedente: fa il commit e il push della propria branch, si sposta nella master branch, fa il merge e poi fa un push sul master online.
A questo punto cosa è successo? La master branch locale di A non è più aggiornata all'ultima versione perché B l'ha appena aggiornata. Quando A finisce sulla sua branch quindi deve:
- spostarsi sulla master branch locale
- fare un pull per aggiornare la sua master branch locale
- spostarsi sulla propria branch (feature1)
- fare il merge dalla master branch
In questo modo A sta integrando il lavoro fatto e finito da B, nella sua branch. Questa operazione però può generare dei conflitti.
I conflitti si verificano quando B modifica uno o più file che anche A ha modificato. In questi casi git non sa come integrare il codice nella branch di destinazione e quindi vanno risolti manualmente.
Facciamo un esempio pratico facile facile. Ipotizziamo che il nostro progetto sia un sito web e che le Feature 1 e 2 siano due pagine html. Per efficienza è stato deciso di utilizzare un singolo file css da importare in entrambe le pagine: style.css. B fa la sua pagina aggiungendo al suo css la classe f1_class per poi caricare il tutto online. A lavora sulla sua pagina e anche lui modifica il file css aggiungendo la classe f2_class. Quando A finisce, fa le operazioni viste nell'elenco precedente generando un conflitto perché il file css è stato modificato da entrambi gli sviluppatori.
Quindi nell'esempio abbiamo:
css sviluppato da B | css sviluppato da A |
body{ color:withe; width:100%; } .f2_class{ float:left; } |
body{ color:withe; width:100%; } .f1_class{ color:red; } |
Al momento del merge avremo la segente risposta di conflitto sul file style.css:
~/gitTest$ git merge master Auto-merging style.css CONFLICT (content): Merge conflict in style.css Merge automatico fallito; risolvi i conflitti ed eseguire il commit del risultato.
Aprendo il file style.css vedremo come viene segnalato il conflitto.
body{ color:withe; width:100%; } <<<<<<< HEAD .f2_class{ float:left; ======= .f1_class{ color:red; >>>>>>> master }
Può sembrare complicato ma in realtà non lo è! Quello che è tra <<<<<<< HEAD e ======== è ciò che ha generato il conflict nella destinazione del merge mentre la parte sottostante tra ======= e >>>>>>> master ciò che lo ha generato nella branch sorgente (nel nostro caso la master).
Per risolvere il conflitto, basterà risistemare il codice rimuovendo la sintassi utilizzata per il conflict
body{ color:withe; width:100%; } .f2_class{ float:left; } .f1_class{ color:red; }
Salvato il file da terminale eseguiamo
git commit
per dire "oh! Ho risolto! Fai il commit!". Si aprirà vim o nano per scrivere il messaggio del commit del merge. Chiuso l'editor abbiamo integrato tutto! Ovviamente può succedere che i file in cui c'è conflitto siano più di uno. Se ciò accade, vanno risolti i conflitti nei vari file prima di fare il commit.
Ora che abbiamo risolto il conflitto, A testerà nuovamente che il progetto nella sua branch locale funzioni con le nuove integrazioni e, in tal caso, farà un merge dalla sua branch feature1 sulla master branch per poi pushare tutto online.
Riepilogo e regole d'oro
Quando si lavora in team la procedura di lavoro è la seguente
- Creare una branch per sviluppare una feature
- Una volta finito lo sviluppo ci si trasferisce sulla master branch e si fa un pull per aggiornarla
- Ci si sposta sulla branch di sviluppo e si fa un merge dalla master branch
- Si risolvono i possibili conflitti (e si fa un push online sul branch di sviluppo)
- Ci si sposta sulla master branch e si fa un merge dalla branch di sviluppo (questa operazione non può generare conflitti perchè risolti al passo precedente)
- Si fa un push dalla master locale alla master online perché abbiamo testato il codice e funziona tutto
Regole d'oro che vanno osservate per lo sviluppo in team con git:
- Cercare di organizzare il progetto in modo tale che lo sviluppo in parallelo di feature diverse porti al minor numero di conflitti possibile
- Prima di una integrazione fare il pull del master
- Non sputtanare la master branch. Le integrazioni vanno fatte sulle branch di sviluppo
- Prima di fare il merge nella master branch, assicurarsi che il progetto funzioni a dovere
Per oggi è tutto. Nel prossimo tutorial vedremo il rebase e le differenze che ci sono con il merge.
Alla prossima!