No. 95

Titolo originale: Get Started with Git

Pubblicato in: Project Management, Workflow & Tools

Scritto da Al Shaw

Se siete sviluppatori o designer, avrete probabilmente sentito parlare di Git e forse saprete che è diventato estremamente popolare, specialmente nella comunità open source. Sebbene sulle prime possa apparire criptico, questo sistema di version control potrebbe cambiare il modo in cui lavorate con il testo, sia che stiate scrivendo del codice sia un romanzo.

Questo articolo copre i motivi per cui il controllo di versione è importante, come installare il version control system Git e come iniziare il vostro primo repository. Una volta che avrete cominciato con Git, vorrete buttarci dentro tutto, dalle app fatte e finite alle bozze dei blog post dal momento che è così facile e versatile.

Perché mi serve il version control?

Mentre è ovvia la ragione per cui team di sviluppo piuttosto numerosi necessitino di un sistema sofisticato per la gestione del codice per tracciare le varie release ed i bug e per evitare di calpestarsi i piedi a vicenda, potrebbe non apparire così chiaro perché dei singoli individui possano avere bisogno di un controllo di versione, specialmente i designer o gli scrittori.

Provate però a pensare ad un sito come Wikipedia, che è costruito attorno alla creazione collaborativa di contenuto. Una delle migliori feature di Wikipedia è la sua capacità di confrontare due versioni di un articolo. Quando lo fate, quello che state eseguendo è un diff, uno dei concetti centrali del controllo di versione. E quando decidete di aggiungere o modificare del contenuto, state facendo un commit di una revisione. Con Git, potete aggiungere funzionalità simili a queste di Wikipedia a qualunque cartella e automaticamente questa comincerà a cercare le variazioni nei file che sono contenuti in essa, anche se non sono file di testo. Nel gergo di Git, quella cartella diventa il vostro repository.

Inoltre, con Git potete fare molto altro. Il branching ed il merging sono strumenti potenti per integrare i cambiamenti senza compromettere il lavoro più stabile che potrebbe, ad esempio, essere attivo su un sito in produzione. I remotes sono copie di un intero repository che vengono trasferite su di una rete e sono molto utili per collaborare a dei progetti con le persone che sono sia nel vostro ufficio sia in internet.

Niente più nomi di file da circo

Come minimo, Git vi risparmierà il tedio di dover trovare un nome per ogni versione del file (ad es., vi eviterà untitled-1-new-v2.html). Nel caso migliore, renderà il vostro lavoro più veloce, salvando le modifiche, gestendo le diverse idee e feature nel vostro progetto e perfino fungendo da strategia di backup. Poiché Git opera in maniera non intrusiva su intere cartelle, non interferirà con il vostro lavoro giornaliero finché non gli chiederete esplicitamente di salvare la snapshot di un progetto in qualunque momento. E rimuovere per sempre Git da una cartella richiede solo un comando.

Git cambia il modo in cui lavorate, rendendo i rischi più economici. A differenza delle features fatte a casaccio come gli “undo multipli” ed i “salvataggi automatici” comuni in molti editor e pacchetti grafici, Git si aspetta che siate voi a controllare come e quando fare un commit dei cambiamenti di un progetto e così facendo permette al vostro progetto di evolversi partendo da uno qualsiasi di questi cambiamenti. Inoltre, fa ciò senza aggiungere altro ciarpame amministrativo attorno al versioning (pensate ai label, ai marker e ai file extra) al vostro progetto. Su qualunque cambiamento si può fare un merge automaticamente, così non dovrete mai ripetervi o cercare di capire come rifare qualcosa che avete sovrascritto. Per questo motivo, Joel Spolsky ha definito Git come un “sistema di controllo dei cambiamenti” invece di un sistema di controllo di versione, perché piuttosto che presentare un progetto linearmente (la mentalità dell'“undo”), Git vede solo un manipolo di commit sempre pronti ad essere rimescolati o amalgamati insieme.

Quando gestite i cambiamenti invece delle versioni, il merging funziona meglio e quindi, potete fare un branch ogni qualvolta i vostri obiettivi organizzativi lo richiedano, perché tornare indietro con i merge è semplicissimo.

All'inizio fu la riga di comando

Git è diverso dalla maggior parte delle applicazioni native dei moderni sistemi operativi poiché (quasi) non ha un'interfaccia utente grafica: è totalmente comandato da riga di comando. Questo potrebbe sembrare scoraggiante all'inizio ma i pochi comandi di uso comune diventeranno presto molto familiari. Se non avete mai usato la riga di comando, vi consiglio caldamente lo screencast PeepCode di Dan Benjamin, Meet the Command Line, come rapido corso intensivo.

Per le persone più inclini all'aspetto visivo, c'è qualche tool grafico per navigare e manipolare i repository di Git. Git è composto da due programmi: uno chiamato gitk per navigare attraverso la storia del vostro progetto, e l'altro chiamato git-gui per aggiungere file e creare commit. Con queste due mini applicazioni, non avrete bisogno di usare molto l'interfaccia a riga di comando, ma rimarrete sorpresi scoprendo quanto sia effettivamente più rapido usare la riga di comando di Git piuttosto che git-gui o gitk, specialmente se caricate il vostro progetto su GitHub, che ha un eccellente browser per il sorgente. Gli utenti di Mac OS X dovrebbero dare un'occhiata a GitX, una versione più carina di gitk/git-gui per visualizzare e modificare i repository. Il wiki officiale ha una lista completa delle interfacce grafiche per Git.

Facciamo chiarezza prima di iniziare

Quando faccio riferimento al terminale, uso prompt> per rappresentare il command prompt e cosa scrivere dopo di esso. Tutto quello che sta sotto alla riga del prompt> è quello che verrà visualizzato dopo aver premuto invio. La shell di Unix è concisa, il che vuol dire che non vi dirà se qualcosa ha avuto esito positivo ma solo se qualcosa è andato storto. Git è abbastanza bravo nel comunicarvi quello che sta facendo, ma non allarmatevi se non tutti i comandi vi danno una risposta.

Installare Git

Ci sono molti modi per ottenere ed installare Git: il più rapido è quello di scaricarlo e compilarlo dal sorgente. Ecco una buona guida passo a passo su come installarlo su Mac OS X. In alternativa, gli utenti Mac possono anche scaricare l'installer grafico di Git ed utilizzarlo per installare Git come ogni altro programma Mac. Su Ubuntu Linux, eseguite apt-get install git-core; gli utenti Windows dovrebbero guardare msysgit.

Il libro gratuito Pro Git ha anche una sezione ben fatta sull'installazione di Git su diverse piattaforme.

Creare un repository

Ora che Git è installato, inizieremo con un repository (o repo, per abbreviare). Per prima cosa, assicuriamoci di averlo installato correttamente:


prompt> git --version
git version 1.7.2.2

Funziona! (Se avete avuto un messaggio diverso, provate a re-installarlo). Ora siamo pronti per creare il nostro primo repo. Per prima cosa, creiamo una nuova cartella in un qualunque punto di Finder e poi andiamo su quella cartella dal terminale:


prompt> cd /Users/alshaw/testrepo

Per far sì che questa cartella diventi un repo di Git, digitate (Le linee che proseguono sulla riga successiva sono indicate da » —Ed.):


prompt> git init
Initialized empty Git repository in /Users/alshaw/»
testrepo/.git/

prompt> git status
# On branch master
#
# Initial commit
#

Dietro le quinte, questo crea una directory nascosta .git all'interno della cartella che terrà traccia dei cambiamenti che farete. Solo per essere sicuri, la testiamo:


prompt> ls -a
. .. .git

Git non toccherà mai i vostri files o farà mai nulla al di fuori della directory .git, a meno che voi non gli diciate di fare diversamente. Se un giorno doveste decidere che quella cartella non dovrà più essere un Git repo, basterà che cancelliate la cartella .git:


prompt> rm -rf .git

Un breve test per controllare di aver veramente tolto Git da “testrepo”:


prompt> git status
fatal: Not a git repository (or any of the parent »
directories): .git

Un altro modo per cominciare a lavorare con un repository consiste nel fare git-clone di un repository esistente. Supponiamo di voler scrivere una nuova app per il web e di voler usare il nuovo eccellente HTML5 Boilerplate di Paul Irish come punto di partenza. Dal momento che Paul ha il suo codice sorgente in un repository Git su GitHub, possiamo andare a prenderlo e cominciare a modificarlo senza lasciare la riga di comando:


prompt> git clone git://github.com/paulirish/»
html5-boilerplate.git
Cloning into html5-boilerplate...
remote: Counting objects: 932, done.
remote: Compressing objects: 100% (820/820), done.
remote: Total 932 (delta 477), reused 196 »
(delta 88)
Receiving objects: 100% (932/932), 1.54 MiB | 349 »
KiB/s, done.
Resolving deltas: 100% (477/477), done.

prompt> cd html5-boilerplate

Ottimo, ora abbiamo l'intera storia del progetto! Andiamo a vedere a cosa ha lavorato Paul usando git-log:


prompt> git log
commit 75b34e5962b155238bcb711e87b34a6409787e78
Author: paulirish <paul.irish@gmail.com>
Date: Tue Aug 24 22:12:59 2010 -0700

minified dd_belated png... you probably dont »
need the full one.
[snip]

Con git-log si può avere una lista di tutti i commit, dello hash SHA1 del commit (una stringa univoca assegnata ad ogni commit), l'autore ed un messaggio che è stato scritto dall'autore per descrivere la modifica.

Se vogliamo esaminare nel dettaglio quello che è cambiato in questa versione, possiamo usare git-diff per confrontarla con le modifiche precedenti. Osserviamo la differenza tra la versione attuale ed un commit precedente. Per fare ciò, useremo git-diff con l'hash id del commit precedente come argomento:


prompt> git diff b59c1cc00e1f6a25a12a224080a70287fa33e4da
[snip]
diff --git a/.htaccess b/.htaccess
index 9879e5e..5451611 100644
--- a/.htaccess
+++ b/.htaccess
@@ -68,7 +68,7 @@ AddType text/x-component htc
# Disabled by default.

# <FilesMatch "\.combined\.(js|css)$">

-# Options +IncludesNOEXEC
+# Options +Includes
# SetOutputFilter INCLUDES
# </FilesMatch>
[snip]

Questa è solo una parte di quel diff, che mostra un cambiamento in questo file .htaccess di esempio. L'autore ha aggiunto una linea (marcata con +) e ne ha cancellata un'altra (marcata con -).

Aggiunte e commit

Ora che abbiamo dato un'occhiata a questo repo, perché non facciamo dei cambiamenti per farlo funzionare nella nostra app? Supponiamo di voler usare Google Ads, così non dovremo ridefinire document.write. Prenderemo questa funzione da plugin.js. Inoltre, elimineremo la regola per la selezione del testo in rosa acceso nel file style.css: non sono sicuro a cosa pensassero quando hanno scritto quella regola.

Lancio il mio editor di testi, cancello quei due pezzi di codice e salvo i file. Torno alla linea di comando ed inserisco:


prompt> git status
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what »

will be committed)
# (use "git checkout -- <file>..." to »
discard changes in working directory)
#
# modified: css/style.css
# modified: js/plugins.js
#

Bene, Git sa che ho fatto dei cambiamenti a quei due file. Ora vogliamo fare una commit dei cambiamenti. Prima però, giusto per divertirci, possiamo vedere cosa sa Git su ciò che abbiamo cambiato. In questo commit abbiamo cancellato diverse righe:


prompt> git diff
diff --git a/css/style.css b/css/style.css
index daf8d54..d47b608 100644
--- a/css/style.css
+++ b/css/style.css
@@ -155,11 +155,6 @@ input:invalid {
.no-boxshadow input:invalid { background-color: »

#f0dddd; }


-/* These selection declarations have to be separate.
- No text-shadow: twitter.com/miketaylr/status/12228805301
- Also: hot pink. */
-::-moz-selection{ background: #FF5E99; color:#fff; »
text-shadow: none; }
-::selection { background:#FF5E99; color:#fff; »
text-shadow: none; }
[snip]

Rosa acceso, eliminato!

La staging area

Prima di fare il nostro primo commit, vi faccio notare che la staging area (conosciuta anche come index) è una particolarità cruciale di Git di cui dovremmo essere a conoscenza. (Questo concetto non è presente in molti altri sistemi di controllo di versione). Se stessimo usando Subversion, adesso faremmo svn commit e registreremmo i cambiamenti fatti. In Git, c'è un ulteriore passo da fare, per un buon motivo. Prima di poter fare commit di un cambiamento, avete bisogno di fare git-add del file a quella che viene chiamata “staging area” perché su di esso vi si possa fare un commit. Potremmo saltare questo passo nel suo insieme scrivendo:


prompt> git commit -am "my message"

(dove il flag -a significa aggiungi/add). Ma la staging area è stata progettata perché possiate eseguire a mano le vostre commit senza badare a quando avete modificato il codice attuale che li compone. In questa tornata di modifiche, abbiamo cambiato due parti totalmente separate del progetto: un blocco CSS ed una funzione JavaScript. Logicamente, dovrebbero essere trasmesse come due commit separate, dal momento che le modifiche non dipendono una dall'altra. La staging area vi aiuta a suddividere le vostre commit per feature piuttosto che per il momento in cui avete modificato il file di testo che le contiene.

Aggiungiamo prima il file CSS:


prompt> git add css/style.css

prompt> git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: css/style.css
#
# Changed but not updated:
# (use "git add <file>..." to update what »
will be committed)
# (use "git checkout -- <file>..." to »

discard changes in working directory)
#
# modified: js/plugins.js
#

Ora, dal momento che abbiamo aggiunto style.css, Git ci dice che possiamo farne la commit:


prompt> git commit -m "killed the hot pink »
selector"
[master e54357d] killed the hot pink selector
1 files changed, 0 insertions(+), 5 deletions(-)

Il buon senso ci impone un rapido controllo:


prompt> git log
commit e54357d66498870072c4348168f16cafcd0496e7
Author: Al Shaw <almshaw@gmail.com>
Date: Wed Aug 25 22:48:29 2010 -0400

killed the hot pink selector
[snip]

Ora noterete che se eseguite git-status solo plugins.js risulta come modificato. Possiamo quindi farne una commit separata con il suo messaggio. Dal momento che Git è effettivamente un change tracker piuttosto che un version tracker, organizzare i commit per feature ci permette di spostare queste feature facilmente in un secondo tempo. Ad esempio, se volete portare un intero insieme di features da un branch all'altro (spiegherò dopo cosa sono i branch), è molto più semplice averli nei loro commit. Per questa ragione, molti sviluppatori adottano una filosofia “commit often” [fare commit spesso, ndt] e tornano in un secondo tempo ad organizzare i commit.

La staging area come soluzione al “problema della copia di lavoro intricata” gratta solo la superficie degli sforzi di Git per farvi tenere il codice organizzato per feature. E c'è dell'altro: a differenza di quello che potrebbe sembrare, Git non vede effettivamente il vostro codice come file discreti, ma al contrario, vede tutto nel vostro repository solo come un grande agglomerato di testo. E' così che tiene traccia dei diff (e dei rename) attraverso i file all'interno di un repository. La natura “stupida” di Git è parte del suo ottimo design. Diventa comodo quando state lavorando a due feature diverse che potrebbero essere nello stesso file e volete separarle in due commit differenti. C'è un'opzione speciale chiamata git add --patch specifica per questi casi, che vi permette di mettere nello “stage” il codice per “agglomerato” piuttosto che per file interi. Ecco un'ottima carrellata di Ryan Tomayko di tutte le possibilità del git index e come usare add --patch.

Un'ultima cosa su git-add di cui non ho ancora parlato: se create un nuovo file che Git non ha mai visto prima, dovrete usare git-add per far sì che Git possa cominciare a tenere traccia di quel file. Questo uso di git-add, tuttavia, non lo metterà direttamente nella staging area. Nell'esempio seguente ho creato un nuovo file nella directory html5-boilerplate, vi ho messo del testo e poi l'ho salvato con il mio editor di testi. Git ha subito notato che c'è un nuovo file nella directory che non fa ancora parte del repo:


prompt> git status
# Untracked files:
# (use "git add <file>..." to include in »
what will be committed)
#
# newfile.txt

Se volete fare un commit di questo file, per prima cosa dovrete aggiungerlo al repo e poi aggiungerlo di nuovo (git add . aggiunge ogni file che trova) per metterlo nella staging area. Questa volta, dal momento che c'è solo un file, useremo il flag -a per aggiungerlo e fare un commit allo stesso tempo:


prompt> git add .
prompt> git commit -am "added some random text"
[master d5c2ed1] added some random text
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 newfile.txt

Con git-init, git-status, git-log, git-add, git-commit e git-clone avete il 90% dei tool di cui avete bisogno per portare a termine le cose con Git. Ma ci sono ancora un po' di cose che possono addolcire la pillola.

Branching e merging

Ad un certo punto, vi verrà in mente una nuova feature sperimentale che sarà troppo grande per un singolo commit. In questo caso, vorrete considerare l'opzione di creare un branch. I nuovi branch sono semplici copie dello stato di un repository a ciascun commit. In effetti, siamo stati su un branch per tutto questo tempo senza saperlo. Il branch che crea Git di default è chiamato “master”. Non c'è nulla di speciale riguardo al master, ma per convenzione è solitamente considerato come il branch “stabile” parallelo ad un branch di sviluppo o sperimentale per nuove feature e bug fix.

Per vedere tutti i branch e crearne di nuovi, si usa git-branch:


prompt> git branch
* master

Nel nostro esempio html5-boilerplate c'è solo un branch locale. Creiamone un altro e spostiamoci lì:


prompt> git checkout -b development
Switched to a new branch 'development'


prompt> git branch
* development
master

Ora abbiamo due branch nel repository locale. L'asterisco connota quello attuale. Git terrà sempre traccia del branch in cui siamo e vi starà finché non verrà esplicitamente fatto un checkout verso un altro branch. Questo sarà importante quando decideremo di fare il merge.

Si noti che quando si effettua il checkout da un branch, i file nella directory di lavoro si sposteranno ovunque ci sarà del codice in quel branch. Con questo intendo dire che Git “modificherà” effettivamente i vostri file perché siano uguali alle versioni presenti nel branch da cui state facendo il checkout e vedrete i cambiamenti “apparire improvvisamente” nel vostro editor di testo. Niente andrà perso finché farete una commit (in un branch o in un altro), assicuratevi semplicemente di aver salvato i files nel vostro editor di testo prima di fare il checkout da un altro branch perché i cambiamenti non Git-aware potrebbero essere sovrascritti.

Supponiamo di aver terminato la nostra nuova feature in questo branch di sviluppo e voglia farne un merge nel nostro master branch (perché è pronto per essere inviato). Per fare il merge dobbiamo sempre fare il checkout dal branch in cui vogliamo fare il merge e poi fare il merge sul branch target al suo interno:


prompt> git checkout master
Switched to branch 'master'

prompt> git merge development
Updating d5c2ed1..9a66cbf
Fast-forward
newfile.txt | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)

Ora tutti i cambiamenti che avete fatto nel development branch sono stati miscelati con quelli del master. Dal momento che questa feature è terminata, cancelleremo il development branch.


prompt> git branch -d development
Deleted branch development (was 9a66cbf).

Lo sviluppatore Vincent Driessen ha creato uno schizzo ben fatto di un workflow di branching di Git che incorpora infiniti branch master e di sviluppo insieme a dei mini feature branch eliminabili.

Un altro suggerimento sul branching: git-checkout ha un altro uso, più controverso che a volte non ha assolutamente a che fare con il branching. In effetti, viene ritenuto un comando distruttivo. Se facessi una modifica a newfile.txt ma non la mandassi in stage, e poi eseguissi git checkout newfile.txt, riporterebbe quel file allo stato in cui era al momento dell'ultimo commit senza alcun undo. Come l'opzione “revert” in Photoshop, tornerà indietro all'ultimo stato in cui avete comunicato qualcosa a Git riguardo al file. Questo può essere pericoloso ma può anche essere utile per tirarvi fuori dalla “tana di un coniglio” (ricordatevi che a Git non interessa quando usate la funzione “salva” dell'applicazione che state usando, ma solo quando usate deliberatamente il comando commit).

In maniera simile, si può usare git-checkout per sradicare un file da un branch e metterlo nel branch attuale. Supponiamo che io mi trovi nel master branch e che io voglia la versione di un file di cui ho fatto il commitment nel development branch. Potrei eseguire git checkout development myfile.txt e rimpiazzerei solo quel file nel branch corrente con la versione development del file. Pensate a git-checkout come ad un merge più distruttivo. Non vi farà domande prima di calpestare il vostro lavoro, quindi usatelo con cautela.

Step successivi

Ci sono molti altri aspetti di Git che vanno oltre lo scopo di questo articolo. Lavorare con i remote repository, ad esempio, è un aspetto vasto ed eccitante del workflow di Git, e la pietra angolare del famoso sito di “social coding” GitHub. Sia che siate un programmatore, un blogger o un designer, Git costituisce un eccellente modo di tenere traccia, condividere e fare un backup del vostro lavoro.

Risorse aggiuntive

Illustrazioni: Carlo Brigatti

Share/Save/Bookmark
 

Discutiamone

Ti sembra interessante? Scrivi tu il primo commento


Cenni sull'autore

Al Shaw

Ritratto di Al ShawAl Shaw è un Designer/Developer presso il famoso sito di news politiche Talking Points Memo, dove ha recentemente creato il TPM PollTracker. Il suo blog è Shhhaw!. Seguitelo su Twitter e GitHub.