Cos'è Git/Cos'è un VCS?
Su questa pagina trovate un'introduzione basilare a
Git. Presupporrò che siate capaci ad installare un
programma, o che l'abbiate già installato (e comunque l'operazione non è più difficile di un "apt-get install
git" o "yum install git"). Cercherò però di spiegarvi cosa sia un VCS, e di convincervi di quanto possa essere
utile usare uno strumento che faciliti lo stoccaggio e la sincronizzazione (nonché di un monte di altre cose)
quando si lavora a un progetto in Latex (come ad esempio una tesi, o un grosso articolo).
Anche il sito che state visitando è stato sviluppato con l'aiuto di Git.
Un VCS (Version Control System) è un programma che
permette di registrare le modifiche fatte a una collezione di file (di testo solitamente) e di condividerle con
altri sviluppatori, solitamente usando come tramite un server (ovvero un computer appositamente configurato e
accessibile a tutti gli sviluppatori). I VCS sono stati inizialmente creati per gestire i sorgenti di progetti
software, e facilitare la coordinazione fra i programmatori. Git è un VCS innovativo sotto molti aspetti
(ed è un cosiddetto VCS Distribuito), e mi
sebbene esistano svariate alternative eccellenti (tipo Mercurial, Bazaar, etc), mi sento di dire che è notevolmente
più semplice e conveniente rispetto al più tradizionale Subversion (per non parlare di CVS che è praticamente
un rudere).
Introduzione
Vediamo subito un esempio di utilizzo. Supponiamo di avere dei file in Latex (o simili), in una directory di nome "Tesi":
~/Tesi $ ls
main.tex capitolo1.tex capitolo2.tex bibliografia.bib
Creiamo una repository locale e registriamo i file attualmente contenuti in essa:
~/Tesi $ git init (crea il database locale)
Initialized empty Git repository in /home/maurizio/Tesi/.git/
~/Tesi $ git add * (segna tutti i file come da aggiungere al prossimo salvataggio)
Initialized empty Git repository in /home/maurizio/Tesi/.git/
~/Tesi $ git commit (registra nel database l'aggiunta dei file)
Ho iniziato a scrivere la mia tesi! (e apre l'editor "Vim" per far scrivere il messagio)
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Committer: Io <maurizio@localhost>
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: bibliografia.bib
# new file: capitolo1.tex
# new file: capitolo2.tex
# new file: main.tex
#
(in Vim usate Ins per iniziare a scrivere, poi Esc e :wq per salvare e uscire)
Se durante l'operazione di scrittura preferite usare un editor diverso da Vim, basta che impostiate
la variabile di ambiente GIT_EDITOR prima di lanciare Git, ad esempio potete aggiungere la seguente riga
dentro ~/.bashrc e ~/.profile per far usare "nano" a Git come editor:
export GIT_EDITOR="nano"
Supponiamo che ora abbiate modificato main.tex, e aggiunto un file intro.tex. Potete salvare questa modifica
con
~/Tesi $ git add intro.tex (segna intro.tex come da aggiungere al prossimo salvataggio)
~/Tesi $ git commit -a (salva nel database tutte le modifiche ai file registrati)
Aggiunto introduzione. (mettiamo un nuovo messaggio di modifica comprensibile)
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Committer: Io <maurizio@localhost>
#
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: intro.tex
# modified: main.tex
#
Notate l'opzione "-a" passata a "git commit", essa dice a git di cercare tutti i file registrati che sono
stati modificati, e di salvare le modifiche. Se ad esempio invece avete modificato tanti file distinti e volete
salvare le modifiche in passi separati (ad esempio perché non c'entrano nulla una con l'altra e volete scrivere
messaggi di commit distinti), potete fare "git add file1.tex" e poi "git commit" (senza il "-a"), e poi magari
"git add file2.tex file3.tex" e poi "git commit", etc.
Se volete cancellare un file, dovete cancellarlo anche dalla repository, e quindi dovete farlo
usando il comando "git rm", mentre dovete usare "git mv" per spostare un file:
~/Tesi $ git rm capitolo2.tex (rimuove capitolo2.tex localmente e lo segna da rimuovere nel database)
~/Tesi $ git mv capitolo1.tex Chap1.tex (rinomina capitolo1.tex localmente e lo segna da rinominare nel database)
~/Tesi $ git commit (per salvare le modifiche, come al solito)
Di fatto questi comandi sono già sufficienti per mantenere una storia delle modifiche localmente, e per poter quindi
tornare indietro, confrontare versioni differenti, etc. Inoltre se ad esempio avete cancellato o danneggiato i file,
potete ripristinarli dall'ultima versione salvata nel database
~/Tesi $ rm *.tex (Ops!! ho cancellato la mia tesi per sbaglio...)
~/Tesi $ git reset --hard (fai sì che i file corrispondano precisamente al database)
Notate che Git (a differenza di altri VCS come Subversion) non richiede necessariamente un server per registrare
le modifiche. E' ad esempio molto comodo lavorare offline salvando le modifiche fatte in locale, e poi eventualmente
salvare su un server remoto tutte le nuove modifiche non appena una connessione internet non sia disponibile.
Sincronizzazione con un computer remoto
Ora Git non è così utile se utilizzato singolarmente e senza poter sincronizzarsi con un server remoto. Come dicevamo
non necessario un server su cui sia installato uno speciale servizio relativo a Git, ma basta un computer su cui
abbiate un account ssh, e su cui sia installato Git (soltanto il programma Git), e se non è installato potete
eventualmente anche installare una copia locale sulla vostra home e configurare Git in modo da fare riferimento
ad essa (anche se è piacevole evitare di dover fare questo).
Come prima cosa, colleghiamoci al server remoto e creiamo un database vuoto, ad esempio che ho l'account "monge"
sul server "linuz.sns.it" ho fatto
~ $ ssh monge@linuz.sns.it
monge@linuz.sns.it's password:
linuz:~ $ mkdir Tesi_backup (creiamo una nuova directory)
linuz:~ $ cd Tesi_backup
linuz:~/Tesi_backup $ git init
Poi, in locale, dobbiamo aggiungere un "remote", ovvero le informazioni relative a una repository remota (che di
fatto in realtà può anche essere in un percorso locale). Le assegnamo come nome "tesi_linuz":
~/Tesi $ git remote add tesi_linuz ssh://monge@linuz.sns.it/~/Tesi_backup (creiamo la remote)
~/Tesi $ git push tesi_linuz master (copiamo la branch "master" nella repository remota)
Counting objects: 6, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 515 bytes, done.
Total 6 (delta 1), reused 0 (delta 0)
Unpacking 6 objects
refs/heads/master: 0000000000000000000000000000000000000000 -> 0bf40572edb2ca302278f38bf5fa5238854432ee
To ssh://monge@linuz.sns.it/~/Tesi_backup
* [new branch] master -> master
La branch "master" è il ramo dell'albero che abbiamo usato inconsciamente fino a questo momento, è possibile creare
altri rami in cui salvare modifiche indipendenti (che possono poi essere integrate nel ramo principale, etc), rimandiamo
a una guida più esaustiva per queste possibilità.
Dopo che la repository remota è stata creata, è stata aggiunta alla repostory locale, e la branch master è stata
salvata in remoto, possiamo dopo ogni modifica locale (ogni "commit") possiamo risincronizzare la copia remota
facendo semplicemente
~/Tesi $ git push tesi_linuz
Se invece da un altro computer, o in un'altra directory del nostro computer, etc. vogliamo "prendere" la copia salvata
in remoto, possiamo fare
~ $ git clone ssh://monge@linuz.sns.it/~/Tesi_backup
che creerà una directory di nome Tesi_backup contenente una repository (sia database che i file) come sono stati
salvati nella repository remota.
Impostazione di una chiave Ssh
Siccome le operazioni di push e pull venegono realizzate da git tramite alcune connessioni al server remoto tramite
ssh, è utile impostare una chiave sul server remoto, per evitare di dover digitare tante volte la password.
Per fare questo basta lanciare
~ $ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/maurizio/.ssh/id_rsa): (Invio)
Enter passphrase (empty for no passphrase): (Invio)
Enter same passphrase again: (Invio)
Your identification has been saved in /home/maurizio/.ssh/id_rsa.
Your public key has been saved in /home/maurizio/.ssh/id_rsa.pub.
The key fingerprint is:
48:f5:b8:e9:4a:b3:6e:b9:2d:ba:eb:68:a5:f1:9e:9c maurizio@aragorn
The key's randomart image is:
+--[ RSA 2048]----+
| . |
| . o |
| . . . |
| . . o |
| . S |
| . . . |
| = o.. |
| oo.++= |
| ..oE*=o. |
+-----------------+
Questo comando crea una chiave privata in ~/.ssh/id_rsa e una pubblica in ~/.ssh/id_rsa.pub, per inserire la
chiave nella lista di quelle accettate dal server remoto basta ad esempio fare
ssh monge@linuz.sns.it "mkdir -p ~/.ssh; cat >>~/.ssh/authorized_keys" < ~/.ssh/id_rsa.pub
monge@linuz.sns.it's password:
(Tollerate che il server vi chieda la password un'ultima volta...)