[GIT] Configurazione di una repository Git

Continuiamo la trattazione sul versioning in Git, richiamando un altro post scritto sull’argomento su questo sito:

Qui viene spiegata la procedura di configurazione locale e remota di una repository Git. Servizi online di hosting molto famosi per il versionamento del vostro codice, gratuiti e a pagamento, sono GitHub, Assembla e BitBucket. Io personalmente utilizzo i primi due: su GitHub versiono i progetti “free” (visto che per renderli privati si paga), su Assembla quelli che voglio rendere privati.

git

Vi linko a tutorial per il versioning su Git, per maggiori approfondimenti:

Vediamo ora gli step per il versionamento su Git. Io uso il Terminale su MacOx, ma la procedura funziona allo stesso modo su Windows, anche se per la generazione delle chiavi SSH dovrete aiutarvi con altri programmi, come Putty.

1. Generazione delle chiavi SSH. Le chiavi SSH servono per stabilire una connessione sicura tra il vostro pc e l’account remoto della repository Git, permettendovi anche l’autenticazione. Dopo aver generato le chiavi sul vostro account remoto (GitHub, per esempio), dovrete copiare nelle Impostazioni la chiave pubblica generata.

cd ~/.ssh
# Ci si sposta nella directory ".ssh" presente sul pc. Se non c'è occorre crearla.

Si generano le chiavi, lanciando il comando ssh-keygen da terminale:

ssh-keygen -t rsa -C "your_email@example.com"
# Crea una nuova chiave SSH con il tuo indirizzo email

# Genera la coppia di chiavi rsa ssh pubblica e privata. A video uscirà un messaggio del genere:
# Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]
# se non digiti nulla e premi invio, verrà salvata la chiave privata nel file id_rsa della cartella .ssh

Il terminale chiederà anche la passphrase, ossia la password:

Enter passphrase (empty for no passphrase): [Type a passphrase]
# Enter same passphrase again: [Type passphrase again]

Il comando genererà la coppia di chiavi pubblica e privata.

Your identification has been saved in /Users/you/.ssh/id_rsa.
# Your public key has been saved in /Users/you/.ssh/id_rsa.pub.
# The key fingerprint is:
# 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db your_email@example.com

A questo punto, digitate il seguente comando per copiare la chiave pubblica, da incollare poi nelle Impostazioni dell’account della vostra repo Git (per esempio, su GitHub, vi è la sezione “Add SSH keys” delle Impostazioni Account):

pbcopy < ~/.ssh/id_rsa.pub
# Copia il contenuto del file id_rsa.pub negli Appunti

Eccovi un riferimento utile per questo step: Generating SSH Keys

2. Installazione di Git. Se non già presente, dovete installare Git sul vostro pc (per capire se è già preinstallato, basta lanciare il comando git sul terminale e vedere se vi elenca i vari comandi previsti dal protocollo). L’installazione è molto semplice: basta scaricare l’ultima release da questo link (Git). Altro riferimento utile per questo step: Set Up Git

3. Creazione di una repository. La prima cosa da fare è creare una repository sull’account remoto (di GitHub o Assembla, per esempio). Lì salverete il contenuto del vostro progetto, sincronizzandolo di volta in volta dai vostri pc. Riferimento: Create a repo.

Adesso passiamo alla configurazione della repo GIT locale alle vostre macchine. Spostatevi con il terminale in corrispondenza della directory root del vostro progetto oppure create una directory di progetto da zero. Per esempio:

mkdir ~/Hello-World
# creo una directory di progetto "Hello-World" nella cartella Users

cd ~/Hello-World
# mi sposto in corrispondenza della cartella di progetto appena creata

git init
# inizializzo la cartella di progetto come repository GIT. 
# Ecco il messaggio che vi verrà visualizzato:

# Initialized empty Git repository in /Users/you/Hello-World/.git/

# creo un file README, in cui inserire le informazioni sul progetto
touch README

# committo il file README creato
git add README
# il comando ADD mette il file README nell'area "stage", ossia non è ancora commitato

git commit -m 'first commit'
# committo le modifiche della repository, lasciando il messaggio "first commit"

Se avete inserito (o erano già presenti) altri file/directory nella directory di root (oltre README), allora dovete aggiungerli tutti nell’area “stage”, prima di committarli, con il comando ADD:

# aggiungo tutti i file all'area "stage"
# dopo l'add metto il punto (.)
git add .

git commit -m 'new commit'

Infine, per salvare il tutto sulla repo remota, ecco i comandi:

git remote add origin https://github.com/username/Hello-World.git
# creo una "origin" remota sulla repository GitHub

git push origin master
# mando i commit al branch "master" su GitHub

[GIT] Il versionamento distribuito con GIT ed un efficiente modello di branching

Vi scrivo oggi di un interessante modello di versionamento che si basa sul famosissimo GIT, repo usata ai primordi per la distribuzione del kernel Linux e negli ultimi tempi come maturo e completo sistema di versioning distribuito. GIT ha attirato gran parte degli sviluppatori di vari linguaggi, tanto da diventare più utilizzato di SVN.

Per la configurazione di GIT e alcune nozioni di base, vi rimando all’ottimo articolo “CVS, SVN e GIT!“.
Per approfondire, il miglior riferimento è il freebook online: http://git-scm.com/book
Carina è anche la semplice guida “git – the simple guide
Personalmente uso il servizio online di GITHUB per versionare i miei progetti. Anche se mi sto trovando molto bene con Assembla, invece, per versionare piccoli progetti su repo SVN. Ho utilizzato Mercurial, ma ho avuto non pochi problemi di compatibilità/bug con vari plugin HG. Se volete versionare su una repo GIT, vi consiglio di installare i client SmartGIT, SourceTree o Tortoise (su Mac, ma alcuni anche per Windows). Su XCode in Mac, il plugin di GIT è già configurato e potete sceglierlo come versionatore del vostro progetto.
Ora, qual è il punto di forza di GIT? Sicuramente la decentralizzazione del codice sorgente. Nell’articolo che vi linko, si enumerano i punti a favore di GIT rispetto a sistemi centralizzati di controllo versione come SubversionWhy you should switch from SubVersione to GIT
In sintesi, come già detto, GIT ha il vantaggio di essere una repo distribuita. Invece di lanciare il comando “svn checkout (url)“, che preleva l’ultima versione della repository di progetto, su GIT si lancia il comando “git clone (url)”  che copia-clona l’intera history di progetto. Ciò significa che il clone non intacca il progetto originale presente sulla repo e uno sviluppatore può autonomamente lavorare su una sua copia, in modalità offline.
Con questo nuovo paradigma è possibile committare in una repository locale anche in assenza di connessione alla rete, cosa che invece non era possibile con SVN, visto che il committ viene fatto interamente sulla repo remota.
Altro vantaggio implicito di questo modello è che il workflow non ha un “single point of failure“. In GIT ciascun membro può pushare su qualsiasi altro server se quello centrale viene corrotto, così che altri membri possono accedere in SSH e ripristinare la working copy in pochi minuti.
Grazie al meccanismo di pull e push, la sincronizzazione tra le repo GIT di ciascun membro è alquanto efficiente. Il meccanismo distribuito permette di creare tante repo private (locali) e sincronizzarle con quelle pubbliche. Un integration manager può pullare tutte le copie pubbliche su un unico “gold repository”, dove ci sarà la versione stabile da deployare in produzione.

 

Una feature ben fatta di GIT è quella che permette il merging tra i branch. Ogni membro del team può creare la propria “feature” su un branch separato e committare/pullare il tutto sul branch condiviso (modello che approfondiremo in seguito).
Quando viene creato un branch in GIT, si può facilmente switchare a quello nuovo per iniziare a svilupparci sopra. I branch si possono riaprire per fixare bug, inserire nuovo codice e poi mergiare il tutto sulla “mainline work“. L’operazione di merge genera pochissimi conflitti sui file.
Esiste una vera e propria “area di sosta” dei file, detta staging area, che non è altro che una cache locale in cui si trovano le modifiche del codice prima del commit. Questa è molto utile per avere una peer-review del lavoro fatto. E’ possibile committare i singoli changeset in seguito. GIT mantiene traccia di “parti di file” (stage files) come delta delle modifiche apportate al codice, che si possono committare selettivamente (patch staging).
Queste modifiche, logicamente separate, possono essere committate separatamente per una facile review del progetto e tale feature è detta cherry-pickable.
Passiamo ora alla descrizione di un efficiente modello di branch di GIT, prendendo come riferimento l’articolo “A successful GIT branching model“.

 

In questo modello esiste una repo denominata “origin” che è quella centrale in cui tutti i membri del team pullano e pushano una versione stabile o condivisa delle modifiche.

 

 

Main branches. La repo centrale contiene due main branches che vivono per tutta la durata del progetto e che si chiamano “master” e “develop“.

 

Il branch origin/master conterrà il codice che verrà messo in produzione. Il branch origin/develop è quello dove vengono pushate tutte le modifiche di sviluppo di ciascun membro del team di sviluppo e che viene continuamente aggiornato fino a quando non si è prodotta una versione stabile da mettere in produzione. È anche detto “branch di integrazione” delle repo distribuite tra i vari sviluppatori.
Quando si reputa stabile, il codice su origin/develop viene mergiato su origin/master.
Supporting branches.
Oltre ai branch “principali”, esistono altre tipologie di branch di vita limitata (detti branch di supporto) che si classificano come segue:
  • Feature branches
  • Release branches
  • Hotfix branches

Feature branches. Si stacca (branch off) da develop e si mergia sempre in esso. Si può chiamare in qualsiasi modo tranne che master, develop, release-* o hotfix-*.
Si chiama anche “topic branches” e contiene gli sviluppi di una features o funzionalità logica del progetto.
Per creare un nuovo feature branch facendo il branch off da develop:
$ git checkout -b myfeature develop
Switched to a new branch "myfeature"
 Per mergiare la feature su develop quando è stata ultimata:
$ git checkout develop
#Switched to branch 'develop'
$ git merge --no-ff myfeature
#Updating ea1b82a..05e9557
#(Summary of changes)
$ git branch -d myfeature
#Deleted branch myfeature (was 05e9557).
$ git push origin develop
Il flag –no-ff (dove “ff” sta per “fast-forward”) evita di “confondere” il codice della features nella mainline di progetto. Molto utile nel caso si voglia ricostruire la storia dei commit.

 

Release branches. Il branch off qui avviene da develop e si può mergiare sia su master che su develop. La naming convention è release-*. Questo tipo di branch supporta la preparazione per un nuovo rilascio in produzione. Infatti, mette a disposizione un insieme di metatag utili per etichettare la build version, la data di rilascio, ecc.
Per creare un release branch da develop, supponendo di stare alla versione 1.1.5 in produzione e di voler passare alla 1.2:
$ git checkout -b release-1.2 develop
#Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
#Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
#[release-1.2 74d9424] Bumped version number to 1.2
#1 files changed, 1 insertions(+), 1 deletions(-)
 A questo punto, possiamo mergiare il release branch sul master:
$ git checkout master
#Switched to branch 'master'
$ git merge --no-ff release-1.2
#Merge made by recursive.
#(Summary of changes)
$ git tag -a 1.2
Per prendere le modifiche fatte nel release branch e portarle in develop:
$ git checkout develop
#Switched to branch 'develop'
$ git merge --no-ff release-1.2
#Merge made by recursive.
#(Summary of changes)
Infine, per rimuovere il release branch:
$ git branch -d release-1.2
#Deleted branch release-1.2 (was ff452fe).

 

Hotfix branches. Il branch off si fa da master e si mergia sia in master che in develop. Il naming convention è hotfix-*.
È come il release branch ma viene creato dopo che si ha uno stato indesiderato in produzione, come un bug critico da risolvere immediatamente. A tal scopo di fa un branch off dal corrispondente tag del master che marca la versione in produzione.

Supponendo di avere in produzione la versione 1.2 su cui è avvenuto un severe bug, un hotfix branch si crea così:

 

$ git checkout -b hotfix-1.2.1 master
#Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
#Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
#[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
#1 files changed, 1 insertions(+), 1 deletions(-)
 I commit delle modifiche per la correzione del bug si effettuano su tale hotfix:
$ git commit -m "Fixed severe production problem"
#[hotfix-1.2.1 abbe5d6] Fixed severe production problem
#5 files changed, 32 insertions(+), 17 deletions(-)
A fine correzione, il merge sul master si fa come un release branch:
$ git checkout master
#Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
#Merge made by recursive.
#(Summary of changes)
$ git tag -a 1.2.1
E per allineare il develop con il bugfix:
$ git checkout develop
#Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
#Merge made by recursive.
#(Summary of changes)
Unica eccezione si ha quando la fix riguarda un release branch esistente. In tal caso, l’hotfix va mergiato su tale release, invece che su develop.
Per rimuovere l’ hotfix branch:
$ git branch -d hotfix-1.2.1
#Deleted branch hotfix-1.2.1 (was abbe5d6).