mar 19 marzo 2024 - Lo Sviluppatore  anno VI

Il sistema di controllo di versione GIT

Condividi

In questo post vedremo il sistema di controllo di versione GIT, il VCS (Version Control System) attualmente in voga nella comunità dei programmatori. GIT è un software open source ed è un ottimo strumento di cooperazione grazie alla sua natura distribuita che consente operazioni veloci  e permette una gestione avanzata dei branch. Ma prima di addentrarci nell’uso pratico di GIT, facciamo un piccolo passo indietro facendo una introduzione sui sistemi di versionamento.

Cosa è un sistema di controllo di versione?

Un sistema di controllo di versione (VCS) consente di gestire le modifiche apportate ai file di un progetto (non necessariamente software) su cui tipicamente lavorano più persone. Scopo di un VCS è quello di realizzare una corretta gestione  delle modifiche garantendo le seguenti caratteristiche:

  •  Reversibilità.
  •  Concorrenza.
  •  Annotazione.

La reversibilità è la capacità di un VCS, di poter sempre tornare indietro in un qualsiasi punto della storia del codice sorgente, ad esempio nel caso in cui ci si è accorti di aver introdotto un errore ed è necessario ripristinare l’ultima versione stabile del software.
La concorrenza permette a più persone di apportare modifiche allo stesso progetto, facilitando il processo di integrazione di pezzi di codice sviluppati da due o più sviluppatori.
L’annotazione è la funzione che consente di aggiungere spiegazioni e riflessioni ulteriori alle modifiche apportate; in pratica è possibile “allegare” alla modifica effettuata, delle note in cui ad esempio si spiega il motivo per cui è stato necessario fare tali modifiche, eventuali criticità o qualsiasi altra informazione che si pensa possa essere utile a tutto il team di lavoro.

Con queste caratteristiche un VCS risolve uno dei problemi più comuni dello sviluppo del software: il timore di modificarlo. Quante volte si sente dire “se qualcosa funziona, non cambiarla” che può apparire come una frase scherzosa ma che poi spesso nella realtà avviene. Ecco, un sistema di versionamento ci permette di liberarci dalla paura di modificare il codice perchè sappiamo che in caso di problemi possiamo sempre “tornare sui nostri passi”.

 

VCS Centralizzati e Distribuiti

GIT è un sistema di versionamento distribuito (DVCS) che si contrappone a quelli centralizzati (CVCS), come ad esempio CVS o SVN. Un VCS centralizzato è un sistema progettato per avere una singola copia completa del repository, ospitato in uno o più server, dove gli sviluppatori salvano le modifiche che hanno fatto.
Ma avere un VCS che dipende completamente da un server centralizzato ha una conseguenza ovvia: se il server o il collegamento va giù, gli sviluppatori non saranno in grado di salvare le modifiche. O peggio ancora, se il repository centrale viene danneggiato, e non esiste il backup, la storia del nostro progetto andrà perso. Inoltre un CVCS può anche essere più lento in quanto se si fa un qualche modifica in locale questa deve essere registrata sul server centrale e quindi tutta l’operazione è strettamente legata alla velocità di connessione col server centrale.

Invece nei sistemi distribuiti e quindi in GIT, ogni sviluppatore ha una propria copia locale di tutto il repository e può salvare le modifiche ogni volta che vuole. Anche in GIT esiste un server remoto che contiene l’intero repository condiviso da tutti gli sviluppatori, ma,  se in un certo momento il server che ospita il repository è giù, gli sviluppatori possono continuare a lavorare senza alcun problema, rimandando la registrazione delle modifiche nel repository condiviso in seguito.

Un’altra differenza tra  i CVCSs, e i DVCSs, specialmente Git, è che sono molto più veloci, poiché le modifiche sono gestite localmente, e l’accesso al disco è più veloce di un accesso alla rete, almeno in condizioni normali.
Le differenze tra i due tipi di sistemi possono essere riassunte come segue: con un CVCS abbiamo una completa dipendenza da un server remoto per svolgere il controllo di versione, mentre con un DVCS il server remoto è solo un’opzione per condividere le modifiche.

 

Istallare GIT

Dopo questa introduzione sui sistemi di versionamento passiamo ad istallare GIT e vederne l’uso pratico

Linux

Su macchine Linux eseguire i seguenti comandi:

sudo apt-get update
sudo apt-get install git

Windows

Su Windows prima è necessario scaricare l’ultima versione dalla pagina ufficiale.

Dopo aver scaricato l’eseguibile, lanciarlo e seguire i passi dell’istallazione premendo il bottone Next > fino ad arrivare alla seguente schermata:

git_install

qui scegli la prima opzione e prosegui fino alla fine dell’istallazione premendo Next > e lasciando le impostazioni di default che vengono proposte.

Una volta terminata l’istallazione nel menu contestuale (tasto destro) si avranno le seguenti voci:

  • Git GUI here
  • Git Bash here”

Noi useremo la console bash. Scegliendo la seconda voce si aprirà una console dove sarà possibile inserire i comandi di GIT

GIT in pratica

Adesso che abbiamo istallato GIT vediamo come si procede per versionare un progetto.

Creare un repository

La prima operazione per poter utilizzare git è quello di creare un repository (repo in breve). Per fare questo posizionarsi nella directory del progetto che si vuole versionare e lanciare il seguente comando:

git init

Abbiamo un repository Git! Si noti che nella directory corrente è stata creata la cartella .git; il repository è  la cartella dove è presente quest’ultima. Dentro questa cartella c’è una sorta di database che contiene i metadati relativi al nostro repository. E’ consigliabile non toccare nulla dentro questa directory se non sia ha  dimestichezza con git.

Creiamo la storia del repository

Git costruisce la storia del repository con il comando commit. Una commit è un’istantanea completa del repository, che viene salvato nel DB. Dopo una commit sarà sempre possibile recuperare lo stato precedente di tutti i file coinvolti.
Quando si effettua una commit, normalmente non si “committa” l’intero repository, ma quei file che magari sono stati coinvolti in una modifica. Quindi prima della commit è necessario fare un’operazione intermedia chiamata  staging, in cui i file vengono aggiunti all’ indice. L’indice di Git (o staging area) è dove i dati che stanno per essere salvati con la commit vengono temporaneamente salvati in attesa della commit vera e propria.  L’immagine seguente mostra il suddetto workflow.

gitWorkflow

 

Mentre lo stesso workflow in termini di stato dei file è il seguente:

 

git-file-lifecycle

Quindi, ricapitolando il tutto in termini di comandi di git, abbiamo ad esempio:

$ git add *.java
$ git add README
$ git commit -m 'Prima Commit'

Il primo comando aggiunge tutti i file .java del nostro ipotetico progetto all’indice, si aggiunge inoltre il file di testo README e infine si fa la commit (specificando un commento con l’opzione -m) che rende le modifiche permanenti nel nostro repository.

Per rimuovere un file da Git, è necessario rimuoverlo dai file versionati (più precisamente, rimuoverlo dall’indice di git) e poi committare. Il comando per fare questo è git rm <nomefile>.

Un comando utile per controllare lo stato dei file del nostro progetto è il seguente:

$ git status

Questo comando mostra i file che sono stati aggiunti all’indice (pronti per la commit) e quelli che ancora sono non versionati, diciamo (untracked file).

Per avere l’intera storia del repository si usa il seguente comando:

$ git log

I repository remoti

Git, come abbiamo visto nell’introduzione, è un VCS distribuito. Questo significa che, a parte quello locale, possiamo avere una copia del repository ospitato in un server remoto che, oltre a rendere pubblico il codice sorgente del progetto, viene utilizzato per consentire la collaborazione di più soggetti allo sviluppo.
La piattaforma più conosciuta per l’hosting di repository GIT è GitHub. Purtroppo, però GitHub non offre archivi privati nella sua versione gratuita. Se avete bisogno di una piattaforma di hosting con repository privati illimitati, è possibile utilizzare Bitbucket. Per poter creare un repository remoto dobbiamo scegliere una delle due soluzioni.

Creare un repository remoto

La prima cosa da fare naturalmente è prima creare il nostro repository remoto (riferirsi alla documentazione specifica del serizio di hosting scelto). Fatto questo, il nostro repo avrà un URL che lo identificherà univocamente, un url del tipo:

https://hosting-service.com/nomeutente/nome-repository

Quando abbiamo il nostro repository remoto dobbiamo collegarlo al nostro repository locale nel seguente modo:

git remote add origin https://hosting-service.com/nomeutente/nome-repository

origin è il nome che viene dato di default al repository remoto.
Adesso siamo pronti ad inviare i file del nostro repository locale a quello remoto. Per fare questo si usa:

git push origin

L’ultima versione dei file inviata al server remoto viene indicata come HEAD. In pratica HEAD rappresenta la versione corrente del nostro progetto.

Clonare un repository remoto

Nel caso in cui invece vogliamo partecipare ad un progetto preesistente per cui quindi esiste già un repository remoto condiviso allora si procede a quella che si chiama una clonazione, in pratica si fa il percorso inverso rispetto al caso descritto nel paragrafo precedente cioè copiare il repository remoto in locale sulla nostra macchina. Per fare questo si usa:

git clone https://hosting-service.com/nomeutente/nome-repository

Aggiornare e incorporare

Assumendo che l’uso di GIT avviene in un ambiente cooperativo in cui più persone lavorano allo stesso progetto, è necessario mantenere aggiornato il nostro repository locale con le modifiche che apportano gli altri membri del gruppo di lavoro. Per fare questo si usa il seguente comando:

git fetch origin

Questo comando scarica gli ultimi aggiornamenti presenti nel repository remoto (dal clone o dall’ultimo fetch effettuato) ma è importante notare che tali modifiche non si fondono con le modifche apportate da noi, tale operazione va fatta manualmente. Esiste però il  comando pull che permette di aggiornare e automaticamente incorporare le nostre modifiche (merge) :

git pull

I branch

Spesse volte è necessario fare degli sviluppi paralleli su un progetto, magari, ad esempio, perchè c’è una versione in produzione da mantenere e nel frattempo è richiesto lo sviluppo di features aggiuntive che al momento non devono andare in produzione. In queste situazioni ci viene aiuto il branching. In pratica viene creata una ramificazione della versione principale (master) che consiste in una versione identica alla principale su cui si può agire però in maniera indipendente. Una volta che si “switcha” la propria versione al branch, le operazione di modifica e commit avvengono appunto nel branch e non nella versione master. Successivamente, quando lo sviluppo parallelo non è più necessario si procede a fare convogliare le modifiche fatte nel branch nella versione master.Ma vediamo i comandi per lavorare con i branch:
Creazione di un nuovo branch e switch del repository (ci si predispone a lavorare sui file del branch):

git checkout -b mio_branch

In questo modo abbiamo creato il branch nel nostro repository locale. Per renderlo disponibile agli altri bisogna mandarlo al repository remoto con:

git checkout -b mio_branch

Per tornare di nuovo al branche principale (master):

git checkout master

Per cancellare un branch:

git checkout -d mio_branch

Per incorporare (merge) le modifiche fatte in un branch nella nostra versione corrente si usa:

git merge nome_branch

C’è la possibilità di vedere le differenze tra due branch prima di fare un merge con:

git diff [branch_sorgente] [branch target]

I tag

Come la maggior parte VCS, Git ha la possibilità di identificare dei punti specifici nella storia del repository come importanti. In genere si usa questa funzionalità per segnare i punti di rilascio (V1.0, e così via). In questa sezione, si apprenderà come elencare i tag disponibili, come creare nuovi tag, e quali sono i diversi tipi di tag.

Lista dei tag disponibili:

git tag

Restituisce l’elenco dei tag in ordine alfabetico.

Creazione di tag

Git usa due tipi di tag :  lightweightannotated.

Un tag lightweight si può considerare come un branch che non è sogetto a cambiamenti, è semplicemente un puntatore ad una specifica versione del repository, mentre un tag annotated fa una copia di tutti gli oggetti memorizzando anche altre informazioni relative a chi ha fatto il tag, l’email e consente di aggiungere delle note al tag. E’ sempre consigliabile usare quest’ultimo tipo di tag salvo che non si voglia creare qualcosa di temporaneo.

Per creare un tag annotated si usa:

git tag -a v1.2 -m "questa è la versione 1.2"

Per creare un tag lightweight, invece:

git tag v1.2a

Conclusioni

Abbiamo visto come i  sistemi di controllo di versione aiutano a soddisfare un importante esigenza degli sviluppatori: poter identificare senza problemi ogni punto di tutta la storia della un progetto e poter tornare a qualsiasi punto in qualsiasi momento e abbiamo visto come GIT mette a disposizione tanti strumenti per rendere la vita degli sviluppatori più semplice, non per niente questo lo rende ad oggi lo strumento di versioning più gettonato nella comunità dei programmatori. Qui abbiamo visto alcune delle features di GIT, per chi volesse approfondire l’argomento, sotto alcune risorse utili.

Risorse

Documentazione ufficiale

git cheat sheet

un’altra cheat sheet

 

Lascia un commento

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

Top