Capitolo 3. Cloniamo

In vecchi sistemi di controllo di versione l’operazione standard per ottenere dei file era il checkout. Ottenete così un insieme di file corrispondenti a un particolare stato precedentemente salvato.

In Git e altri sistemi distribuiti di controllo versione, l’operazione standard è il clonaggio. Per ottenere dei file si crea un clone di tutto il deposito. In altre parole, diventate praticamente un mirror del server centrale. Tutto ciò che può fare il deposito centrale, potete farlo anche voi.

Sincronizzazione tra computers

Posso tollerare l’idea di creare degli archivi tar o di utilizzare rsync per backup di base. Ma a volte lavoro sul mio laptop, altre volte sul mio desktop, e può darsi che nel frattempo le due macchine non si siano parlate.

Inizializzate un deposito Git e fate un commit dei vostri file su una macchina. Poi sull’altra eseguite:

$ git clone altro.computer:/percorso/verso/il/file

per creare una seconda copia dei file in un deposito Git. Da adesso in avanti,

$ git commit -a
$ git pull altro.computer:/percorso/verso/il/file HEAD

trasferirà lo stato dei file sull’altro computer aggiornando quello su cui state lavorando. Se avete recentemente fatto delle modifiche conflittuali dello stesso file, Git ve lo segnalerà e dovrete ripetere nuovamente il commit, dopo che avrete risolto il conflitto.

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file:

$ git init
$ git add .
$ git commit -m "Commit iniziale"

Sul server centrale inizializzate un deposito nudo (nudo nella terminologia Git) in una cartella qualunque:

$ mkdir proj.git
$ cd proj.git
$ git init --bare
$ touch proj.git/git-daemon-export-ok

Se necessario, lanciate il daemon:

$ git daemon --detach  # potrebbe già essere in esecuzione

Per servizi di hosting Git, seguite le istruzioni per il setup del deposito Git che inizialmente sarà vuoto. Tipicamente bisognerà riempire un formulario in una pagina web.

Trasferite il vostro progetto sul server centrale con:

$ git push git://server.centrale/percorso/fino/a/proj.git HEAD

Per ottenere i file sorgente, uno sviluppatore deve eseguire:

$ git clone git://server.centrale/percorso/fino/a/proj.git

Dopo aver fatto delle modifiche, lo sviluppatore le salva in locale:

$ git commit -a

Per aggiornare alla versione corrente:

$ git pull

Tutti i conflitti nel momento del merge devono essere risolti e validati:

$ git commit -a

Per inviare le modifiche locali al deposito centrale:

$ git push

Se il server principale ha nuove modifiche introdotte da altri sviluppatori, il push fallisce et lo sviluppatore deve aggiornarsi all’ultima versione, risolvere eventuali conflitti , e provare di nuovo.

Perché i comandi pull e push precedenti funzionino bisogna avere accesso SSH. Comunque, chiunque può vedere il codice sorgente digitando:

$ git clone git://server.centrale/percorso/fino/a/proj.git

Il protocollo nativo git è come l’HTTP: non c'è nessuna autenticazione, così che tutti possono ottenere il progetto. Quindi, per default, push è proibito con protocollo git.

File sorgente segreti

Per un progetto chiuso, omettete il comando touch, e assicuratevi di mai creare un file chiamato git-daemon-export-ok. Il deposito in questo caso non potrà più essere ottenuto con il protocollo git; solo chi ha accesso SSH potrà vederlo. Se tutti i vostri deposito sono chiusi, lanciare il daemon git non è necessario perché la comunicazione avviene via SSH.

Depositi nudi

Un deposito nudo (bare repository) si chiama così perché non possiede una cartella di lavoro; contiene solo i file che sono solitamente nascosti nella sottocartella .git. In altre parole, mantiene unicamente la storia del progetto, e e non conserva nessuna versione.

Un deposito nudo gioca un ruolo simile a quello di un server principale in un sistema di controllo di versione centralizzato: è dove è localizzato il vostro progetto. Altri sviluppatori clonano il nostro progetto da lì, e vi trasferiscono gli ultimi modifiche ufficiali. Tipicamente si trova su un server che non fa altro che distribuire dati. Lo sviluppo avviene nei cloni, così che il deposito principale non ha bisogno di una cartella di lavoro.

Molti comandi git non funzionano per depositi nudi, a meno che la variabile globale GIT_DIR non viene definita con il percorso al deposito, o si utilizza l’opzione --bare.

Push vs pull

Perché abbiamo introdotto il comando push, invece di affidarci al più familiare comando pull? Prima di tutto il comando pull non funziona con depositi nudi: in questo caso bisogna invece usare fetch, un comando che discuteremo più tardi. Ma anche se avessimo un deposito normale sul server centrale, usare pull sarebbe sarebbe scomodo. Bisognerebbe per prima cosa connettersi al server e poi dare come argomento a pull l’indirizzo della macchina dalla quale vogliamo ottenere le modifiche. I firewalls potrebbero interferire nel processo, e cosa faremmo se non avessimo nemmeno accesso shell al server?

In ogni caso, questo caso a parte, vi scoraggiamo l’uso di push per via della confusione che potrebbe generare quando la destinazione ha una cartella di lavoro.

In conclusione, mentre state imparando ad usare Git, usate push solo se la destinazione è un deposito nudo; altrimenti usate pull.

Fare il forking di un progetto

Stufi del modo in cui un progetto è amministrato? Pensate che potreste fare un lavoro migliore? In questo caso, dal vostro server eseguite:

$ git clone git://server.principale/percorso/verso/i/files

Informate ora tutti del vostro fork del progetto sul vostro server.

In seguito potete includere le modifiche provenenti dal progetto originale con:

$ git pull

Il sistema definitivo di salvataggio

Volete degli archivi ridondanti e geograficamente distribuiti? Se il vostro progetto ha moti sviluppatori non c'è bisogno di fare niente! Ogni clone del vostro codice è effettivamente un backup. Non solo dello stato corrente, ma dell’intera storia del vostro progetto. Grazie al hashing crittografico, se qualcuno dovesse avere un clone corrotto, sarà individuato non appena si connetterà agli altri.

Se il vostro progetto non è molto popolare, trovate il più alto numero possibile di server che possano ospitare dei cloni.

Il vero paranoico dovrebbe anche sempre annotarsi l’ultimo codice SHA1 dell’HEAD di 20 bytes in un posto sicuro. Deve essere sicuro, non privato. Ad esempio, pubblicarlo in un giornale funzionerebbe bene, visto che sarebbe difficile realizzare un attacco modificando tutte le copie del giornale.

Multi-tasking alla velocità della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalità. In questo caso fate un commit del progetto e eseguite:

$ git clone . /una/nuova/cartella

Grazie ai collegamenti fisici, i cloni locali richiedono meno tempo e spazio che i backup usuali.

Potete ora lavorare simultaneamente su due funzionalità indipendentemente. Ad esempio, potete modificare un clone mentre l’altro sta compilando. Ad ogni modo, potete validare con commit le vostre modifiche e importare con pull i cambiamenti dagli altri cloni:

$ git pull /il/mio/altro/clone HEAD

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo di versione, e vi manca disperatamente Git? In tal caso, inizializzate un deposito Git nella vostra cartella di lavoro:

$ git init
$ git add .
$ git commit -m "Commit iniziale"

poi clonatelo:

$ git clone . /una/nuva/cartella

Ora navigate alla nuova cartella e lavorate da qua, utilizzando Git come volete. Di tanto in tanto, quando volete sincronizzarvi con gli altri, recatevi nella cartella originale, sincronizzate utilizzando l’altro sistema di controllo di gestione, e poi digitate:

$ git add .
$ git commit -m "Sincronizzazione con gli altri"

Andate quindi nella nuova cartella e lanciate:

$ git commit -a -m "Descrizione delle mie modifiche"
$ git pull

La procedura per condividere le vostre modifiche con gli altri dipende d’altro canto dall’altro sistema di controllo di versione. La nuova cartella contiene i file con i vostri cambiamenti. Lanciate qualsiasi comando dell’altro sistema di controllo di gestione sia necessario per inviarli al deposito centrale.

Subversion, che è forse il migliore sistema di gestione di versione centralizzato, è utilizzato da innumerevoli progetti. Il comando git svn automatizza la procedura precedente per i depositi Subversion, e può anche essere usato per esportare un progetto Git in un deposito Subversion.

Mercurial

Mercurial è un sistema di controllo di versione che può funzionare in tandem con Git in modo quasi trasparente. Con il plugin hg-git un utente di Mercurial può, senza svantaggi, inviare a (push) e ottenere (pull) da un deposito Git.

Scaricate il plugin hg-git con Git:

$ git clone git://github.com/schacon/hg-git.git

o Mercurial:

$ hg clone http://bitbucket.org/durin42/hg-git/

Sfortunatamente, non sembra ci sia un plugin analogo per Git. Per questa ragione, mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositi principali. Nel caso di un progetto Mercurial di solito un volontario mantiene in parallelo un deposito Git che accomoda utenti Git, mentre, grazie al plugin hg-git, un progetto Git accomoda automaticamente utenti Mercurial.

Nonostante il plugin può convertire un deposito Mercurial in uno Git trasferendolo in un deposito vuoto, questo è più facile con lo script hg-fast-export.sh, ottenibile da:

$ git clone git://repo.or.cz/fast-export.git

Per fare una conversione, in una nuovo cartella eseguite:

$ git init
$ hg-fast-export.sh -r /depot/hg

dopo aver aggiunto lo script al vostro $PATH.

Bazaar

Menzioniamo brevemente Bazaar perché è il sistema di controllo di versione distribuito gratuito più popolare dopo Git e Mercurial.

Bazaar ha il vantaggio del senno di poi, visto che è relativamente giovane; i suoi disegnatori hanno potuto imparare dagli errori commessi nel passato e evitare gli scogli storici. Inoltre, i suoi sviluppatori sono attenti a questioni come la portabilità e l’interoperabilità con altri sistemi di controllo di versione.

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare con depositi Git in una certa misura. Il programma tailor converte depositi Bazaar in depositi Git, e può farlo in maniera incrementale, mentre bzr-fast-export è fatto per le conversioni uniche.

Perché utilizzo Git

Ho originariamente scelto Git perché avevo sentito che era in grado di gestire l’inimmaginabilmente ingestibile sorgente del kernel Linux. Non ho mai sentito la necessità di cambiare. Git mi ha servito un servizio impeccabile, e non sono mai stato colto alla sprovvista dai suoi limiti. Siccome utilizzo primariamente Linux, i problemi che appaiono sulle altre piattaforme non mi concernono.

In più preferisco programmi in C e scripts in bash rispetto agli eseguibili tipo gli scripts Python: ci sono meno dipendenze, e sono dipendente all’alta velocità di esecuzione.

Ho riflettuto a come migliorare Git, arrivando fino al punto di scrivere la mia propria versione, ma solo come un esercizio accademico. Anche se avessi completato il mio progetto, sarei rimasto a Git comunque, visto che i vantaggi sarebbero stati minimi per giustificare l’utilizzazione di un sistema solitario.

Naturalmente, i vostri bisogni e richieste probabilmente differiscono dai miei, e quindi potreste trovarvi meglio con un altro sistema. Nonostante ciò, non potete sbagliarvi scegliendo Git.