50
Git Magic Ben Lynn Agosto 2007 Prefazione Git è il coltellino svizzero per il controllo di versioni. Uno strumento per la gestione di revisioni affidabile, versatile e multifunzionale, ma la cui flessibilità ne rende difficile l’apprendimento, e ancora di più la padronanza. Come osserva Arthur C. Clarke, qualunque tecnologia sufficientemente avanzata è indistinguibile dalla magia. Questo è un buon atteggiamento con cui approc- ciare Git: i novizi possono ignorare i suoi meccanismi interni e utilizzare Git come fosse una bacchetta magica con cui meravigliare gli amici e far infuriare gli avversari. Invece di dilungarci nei dettagli, vedremo quali istruzioni vanno usate per ot- tenere risultati specifici. In seguito, grazie all’uso ripetuto, capiremo il funzion- amento di ognuno dei trucchi, e come possono essere combinati per sopperire alle nostre necessità. • Cinese semplificato: JunJie, Meng e JiangWei. Conversione in Cinese tradizionale tramite cconv -f UTF7-CN -t UTF8-TW. • Francese: Alexandre Garel, Paul Gaborit, e Nicolas Deram. Anche scari- cabile da itaapy. • Tedesco: Benjamin Bellee e Armin Stebich; anche scaricabile dal sito web di Armin. • Italiano: Mattia Rigotti • Portoghese: Leonardo Siqueira Rodrigues [formato ODT]. • Russo: Tikhon Tarnavsky, Mikhail Dymskov, e altri. • Spagnolo: Rodrigo Toledo e Ariset Llerena Tapia. • Ucraino: Volodymyr Bodenchuk. • Vietnamita: Trần Ngọc Quân; anche scaricabile dal suo sito web. scarica- bile dal suo sito 1

Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

  • Upload
    others

  • View
    20

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Git Magic

Ben Lynn

Agosto 2007

Prefazione

Git egrave il coltellino svizzero per il controllo di versioni Uno strumento per lagestione di revisioni affidabile versatile e multifunzionale ma la cui flessibilitagravene rende difficile lrsquoapprendimento e ancora di piugrave la padronanza

Come osserva Arthur C Clarke qualunque tecnologia sufficientemente avanzataegrave indistinguibile dalla magia Questo egrave un buon atteggiamento con cui approc-ciare Git i novizi possono ignorare i suoi meccanismi interni e utilizzare Gitcome fosse una bacchetta magica con cui meravigliare gli amici e far infuriaregli avversari

Invece di dilungarci nei dettagli vedremo quali istruzioni vanno usate per ot-tenere risultati specifici In seguito grazie allrsquouso ripetuto capiremo il funzion-amento di ognuno dei trucchi e come possono essere combinati per sopperirealle nostre necessitagrave

bull Cinese semplificato JunJie Meng e JiangWei Conversione in Cinesetradizionale tramite cconv -f UTF7-CN -t UTF8-TW

bull Francese Alexandre Garel Paul Gaborit e Nicolas Deram Anche scari-cabile da itaapy

bull Tedesco Benjamin Bellee e Armin Stebich anche scaricabile dal sito webdi Armin

bull Italiano Mattia Rigotti

bull Portoghese Leonardo Siqueira Rodrigues [formato ODT]

bull Russo Tikhon Tarnavsky Mikhail Dymskov e altri

bull Spagnolo Rodrigo Toledo e Ariset Llerena Tapia

bull Ucraino Volodymyr Bodenchuk

bull Vietnamita Trần Ngọc Quacircn anche scaricabile dal suo sito web scarica-bile dal suo sito

1

bull Pagina web individuale simplice documento HTML senza CSS

bull File PDF versione stampabile

bull Pacchetto Debian pacchetto Ubuntu ottenete rapidamente una copialocale Pratico quando questo server non egrave raggiungibile

bull Libro vero e proprio [Amazoncom] 64 pagine 1524cm x 2286cm biancoe nero in inglese Pratico in caso di mancanza di elettricitagrave

Grazie

Voglio ringraziare le persone che hanno prestato il loro lavoro per tradurre questepagine Sono grato di poter raggiungere un numero piugrave ampio di lettori grazieagli sforzi delle persone appena citate

Dustin Sallings Alberto Bertogli James Cameron Douglas LivingstoneMichael Budde Richard Albury Tarmigan Derek Mahar Frode AannevikKeith Rarick Andy Somerville Ralf Recker Oslashyvind A Holm Miklos VajnaSeacutebastien Hinderer Thomas Miedema Joe Malin e Tyler Breisacher hannocontribuito con le loro correzioni e miglioramenti

Franccedilois Marier mantiene il pacchetto Debian creato originariamente da DanielBaumarr

Sono anche grato a molti altri per i loro sostegno e incoraggiamenti Sono tentatodi menzionarvi qui ma rischierei di alzare eccessivamente le vostre aspettative

Se per errore ho dimenticato di menzionare qualcuno fatemelo per favore sapereo mandatemi semplicemente una patch

Licenza

Questo manuale egrave pubblicato sotto la licenza GNU General Public License ver-sion 3 Naturalmente il codice sorgente egrave disponibile come deposito Git e sipuograve ottenere digitando

$ git clone gitrepoorczgitmagicgit Crea la cartellagitmagic

oppure da altri server

$ git clone gitgithubcomblynngitmagicgit$ git clone gitgitoriousorggitmagicmainlinegit$ git clone httpscodegooglecompgitmagic$ git clone gitgitassemblacomgitmagicgit$ git clone gitbitbucketorgblynngitmagicgit

2

GitHub Assembla e Bitbucket supportano deposito privati gli ultimi due sonogratuiti

Introduzione

Farograve uso di unrsquoanalogia per introdurre il controllo di versione Fate riferimentoalla pagina wikipedia sul controllo di versione per una spiegazione piugrave sobria

Il lavoro egrave gioco

Ho giocato ai videogiochi quasi tutta la mia vita Per contro ho iniziato adutilizzare sistemi di controllo di versione solo da adulto Sospetto di non essereil solo e il paragone tra i due puograve rendere alcuni concetti piugrave facile da spiegaree comprendere

Pensate alla scrittura di codice o documenti come ad un videogioco Non appenaavete fatto progressi sostanziali egrave desiderabile salvare il vostro lavoro Per fareciograve cliccate sul pulsante Salva del vostro fidato editor

Ma questo sovrascriveragrave la versione precedente del documento Egrave come queivecchi videogiochi in cui si poteva salvare la partita ma senza poter ritornare auno stato precedente del gioco Il che era un peccato percheacute il vostro salvataggioprecedente avrebbe potuto trovarsi ad un punto particolarmente divertente delgioco che avreste magari voluto rivisitare in futuro O ancora peggio se il vostrosalvataggio piugrave recente si fosse rivelato essere uno stato da cui egrave impossibilevincere il gioco obbligandovi a ricominciare la partita da zero

Controllo di versione

Quando modificate un documento di cui volete conservare le vecchie versionipotete Salvare comehellip sotto un nome di file diverso oppure copiare il file inunrsquoaltra cartella prima di salvarlo Potreste anche comprimere queste copiedel file se volete risparmiare spazio su disco Questa egrave una forma primitivae inefficiente forma di controllo di versione I videogiochi hanno miglioratoquesto punto molto tempo fa provvedendo in molti casi multiple possibilitagrave disalvataggio automaticamente ordinate temporalmente

Rendiamo il problema un porsquo piugrave complicato Immaginate di avere un un gruppodi file che vanno insieme come il codice sorgente di un progetto o i file per un sitoweb In questo caso se volete conservare una vecchia versione dovete archiviareuna directory intera Conservare diverse versioni a mano non egrave scomodo ediventa rapidamente impraticabile

3

Nel caso di alcuni videogiochi il salvataggio di una partita consiste effettiva-mente in una directory contenente diversi file Questi giochi nascondono questodettaglio al giocatore e presentano una comoda interfaccia per gestire le diverseversioni di tale cartella

I sistemi di controllo di versione non sono niente piugrave di questo Hanno tuttiuna comoda interfaccia per gestire una directory piena di file Potete salvare lostato della directory di tanto in tanto e piugrave tardi potete caricare ognuno deglistati precedentemente salvati A differenza della maggioranza di videogiochiconservano in maniere intelligente lo spazio Tipicamente pochi file alla voltacambiano da una versione alla successiva Si puograve quindi risparmiare spaziosalvando le differenze invece di fare nuove copie complete

Controllo distribuito

Immaginate ora un videogioco difficilissimo Cosigrave difficile da terminare che moltiesperti giocatori da tutto il mondo decidono di collaborare e condividere le loropartite salvate per cercare di venirne a capo Gli Speedrun sono un esempio con-creto di questa pratica dei giocatori si specializzano ognuno a giocare un livellodello stesso gioco nel miglior modo possibile e collaborano cosigrave per ottenere deirisultati incredibili

Come costruireste un sistema che permetta loro di accedere facilmente ai sal-vataggi degli altri E che permetta di caricarne di nuovi

Nel passato ogni progetto usava un sistema di controllo di versione centraliz-zato Un server centrale unico da qualche parte manteneva tutte le partitesalvate Ogni giocatore conservava al massimo qualche salvataggio sul propriocomputer Quando un giocatore aveva intenzione di avanzare nel gioco scari-cava il salvataggio piugrave recente dal server centrale giocava per un porsquo salvava ericaricava sul server i progressi ottenuti cosigrave che ognuno potesse usufruirne

Ma che cosa succedeva se un giocatore per qualche ragione voleva accedere aduna vecchia partita salvata Forse percheacute la versione piugrave attuale si trovava inuno stato da cui non era piugrave possibile vincere il gioco percheacute qualcuno aveva di-menticato di raccogliere un oggetto al terzo livello e ora era necessario ritrovarelrsquoultima partita salvata in un momento in cui la partita egrave ancora completabile Omagari si desiderava paragonare due partite salvate precedentemente per vederequanto progresso avesse fatto un giocatore particolare

Potrebbero esserci molte ragioni per voler recuperare una vecchia versione mail risultato egrave sempre lo stesso era necessario chiederla al server centrale E piugravepartite salvate erano necessarie piugrave dati era necessario trasmettere

La nuova generazione di sistemi di controllo di versione di cui Git fa parte sonodetti sistemi distribuiti e possono essere pensati come una generalizzazione deisistemi centralizzati Quando i giocatori scaricano dal server centrale ricevono

4

tutti i giochi salvati non solo lrsquoultima Egrave come se fossero un mirror del servercentrale

Questa operazione iniziale di clonaggio puograve essere costosa soprattutto se crsquoegrave unalunga storia di salvataggi precedenti Ma a lungo termine egrave una strategia cheripaga Un beneficio immediato egrave che quando per qualche ragione si desideraun salvataggio precedente non egrave necessario comunicare con il server centrale

Una sciocca superstizione

Una credenza popolare vuole che i sistemi distribuiti non siano adatti a progettiche richiedono un deposito centrale ufficiale Niente potrebbe essere piugrave lontanodalla veritagrave Fotografare qualcuno non ne ruba lrsquoanima Similarmente clonareun deposito principale non ne diminuisce lrsquoimportanza

Una buona prima approssimazione egrave che tutto ciograve che puograve fare un sistema dicontrollo di versione centralizzato puograve essere fatto meglio da un sistema dis-tribuito ben concepito Le risorse di rete sono semplicemente piugrave costose che lerisorse locali Nonostante vedremo piugrave in lagrave che ci sono alcuni svantaggi asso-ciati agli approcci distribuiti ci sono meno probabilitagrave di fare paragoni sbagliatecon questa approssimazione

Un piccolo progetto potrebbe non necessitare di tutte le funzionalitagrave offerte daun tale sistema ma il fatto di usare un sistema difficilmente estensibile perprogetti piccoli egrave come usare il sistema di numerazione romano per calcoli connumeri piccoli

In aggiunta il vostro progetto potrebbe crescere al di lagrave delle vostre previsioniiniziali Usare Git dallrsquoinizio egrave come avere sempre con se un coltellino svizzeroanche se lo utilizzate primariamente per aprire delle bottiglie Quel giorno incui avrete disperatamente bisogno un cacciavite sarete felici di avere piugrave di unsemplice apribottiglie

Merge di conflitti

Per questo argomento la nostra analogia basata sui videogiochi inizia ad esseretirata per i capelli Ritorniamo quindi invece al caso della formattazione di undocumento

Immaginiamo che Alice inserisce una linea di codice allrsquoinizio di un file e Bobne aggiunge una alla fine della propria copia Entrambi caricano le loro modi-fiche nel deposito La maggior parte dei sistemi decideranno una linea drsquoazioneragionevole accettare e fondere (merge) entrambe le modifiche cosigrave che sia lemodifiche di Alice e Bob sono applicate

Ma supponiamo ora che Alice e Bob hanno fatto distinte modifiche alla stessalinea del documento Egrave impossibile procedere senza intervento umano La sec-

5

onda persona a caricare il file viene informata di un conflitto di merge e bisognascegliere una modifica piuttosto che un altra oppure riscrivere interamente lariga

Situazioni piugrave complesse possono presentarsi Sistemi di controllo di versioni sipossono occupare autonomamente dei casi piugrave semplici et lasciano i casi difficiliallrsquointervento umano Generalmente questo comportamento egrave configurabile

Trucchi di base

Piuttosto che immergerci nel mare di comandi di Git bagniamoci un porsquo i piedicon i seguenti esempi elementari Nonostante la loro semplicitagrave ognuno di loroegrave utile In effetti durante i miei mesi iniziali drsquoutilizzazione di Git non mi sonomai avventurato al di lagrave di del materiale in questo capitolo

Salvare lo stato corrente

Siete sul punto di fare qualcosa di drastico Prima di proseguire catturate lostato di tutti i file nella directory corrente

$ git init$ git add $ git commit -m Il mio primo backup

Qualora le cose dovessero andare per il peggio potrete sempre ripristinare laversione salvate

$ git reset --hard

Per salvare un nuovo state

$ git commit -a -m Un altro backup

Aggiungere rimuovere e rinominare file

Le istruzioni che abbiamo appena visto tengono traccia solo dei file che eranopresenti nel momento dellrsquoesecuzione di git add Ma se aggiungete nuovi file osottocartelle dovrete dirlo a Git

$ git add readmetxt Documentation

Analogamente se volete che Git ignori alcuni file

$ git rm kludgeh obsoletec$ git rm -r incriminatingevidence

6

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 2: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

bull Pagina web individuale simplice documento HTML senza CSS

bull File PDF versione stampabile

bull Pacchetto Debian pacchetto Ubuntu ottenete rapidamente una copialocale Pratico quando questo server non egrave raggiungibile

bull Libro vero e proprio [Amazoncom] 64 pagine 1524cm x 2286cm biancoe nero in inglese Pratico in caso di mancanza di elettricitagrave

Grazie

Voglio ringraziare le persone che hanno prestato il loro lavoro per tradurre questepagine Sono grato di poter raggiungere un numero piugrave ampio di lettori grazieagli sforzi delle persone appena citate

Dustin Sallings Alberto Bertogli James Cameron Douglas LivingstoneMichael Budde Richard Albury Tarmigan Derek Mahar Frode AannevikKeith Rarick Andy Somerville Ralf Recker Oslashyvind A Holm Miklos VajnaSeacutebastien Hinderer Thomas Miedema Joe Malin e Tyler Breisacher hannocontribuito con le loro correzioni e miglioramenti

Franccedilois Marier mantiene il pacchetto Debian creato originariamente da DanielBaumarr

Sono anche grato a molti altri per i loro sostegno e incoraggiamenti Sono tentatodi menzionarvi qui ma rischierei di alzare eccessivamente le vostre aspettative

Se per errore ho dimenticato di menzionare qualcuno fatemelo per favore sapereo mandatemi semplicemente una patch

Licenza

Questo manuale egrave pubblicato sotto la licenza GNU General Public License ver-sion 3 Naturalmente il codice sorgente egrave disponibile come deposito Git e sipuograve ottenere digitando

$ git clone gitrepoorczgitmagicgit Crea la cartellagitmagic

oppure da altri server

$ git clone gitgithubcomblynngitmagicgit$ git clone gitgitoriousorggitmagicmainlinegit$ git clone httpscodegooglecompgitmagic$ git clone gitgitassemblacomgitmagicgit$ git clone gitbitbucketorgblynngitmagicgit

2

GitHub Assembla e Bitbucket supportano deposito privati gli ultimi due sonogratuiti

Introduzione

Farograve uso di unrsquoanalogia per introdurre il controllo di versione Fate riferimentoalla pagina wikipedia sul controllo di versione per una spiegazione piugrave sobria

Il lavoro egrave gioco

Ho giocato ai videogiochi quasi tutta la mia vita Per contro ho iniziato adutilizzare sistemi di controllo di versione solo da adulto Sospetto di non essereil solo e il paragone tra i due puograve rendere alcuni concetti piugrave facile da spiegaree comprendere

Pensate alla scrittura di codice o documenti come ad un videogioco Non appenaavete fatto progressi sostanziali egrave desiderabile salvare il vostro lavoro Per fareciograve cliccate sul pulsante Salva del vostro fidato editor

Ma questo sovrascriveragrave la versione precedente del documento Egrave come queivecchi videogiochi in cui si poteva salvare la partita ma senza poter ritornare auno stato precedente del gioco Il che era un peccato percheacute il vostro salvataggioprecedente avrebbe potuto trovarsi ad un punto particolarmente divertente delgioco che avreste magari voluto rivisitare in futuro O ancora peggio se il vostrosalvataggio piugrave recente si fosse rivelato essere uno stato da cui egrave impossibilevincere il gioco obbligandovi a ricominciare la partita da zero

Controllo di versione

Quando modificate un documento di cui volete conservare le vecchie versionipotete Salvare comehellip sotto un nome di file diverso oppure copiare il file inunrsquoaltra cartella prima di salvarlo Potreste anche comprimere queste copiedel file se volete risparmiare spazio su disco Questa egrave una forma primitivae inefficiente forma di controllo di versione I videogiochi hanno miglioratoquesto punto molto tempo fa provvedendo in molti casi multiple possibilitagrave disalvataggio automaticamente ordinate temporalmente

Rendiamo il problema un porsquo piugrave complicato Immaginate di avere un un gruppodi file che vanno insieme come il codice sorgente di un progetto o i file per un sitoweb In questo caso se volete conservare una vecchia versione dovete archiviareuna directory intera Conservare diverse versioni a mano non egrave scomodo ediventa rapidamente impraticabile

3

Nel caso di alcuni videogiochi il salvataggio di una partita consiste effettiva-mente in una directory contenente diversi file Questi giochi nascondono questodettaglio al giocatore e presentano una comoda interfaccia per gestire le diverseversioni di tale cartella

I sistemi di controllo di versione non sono niente piugrave di questo Hanno tuttiuna comoda interfaccia per gestire una directory piena di file Potete salvare lostato della directory di tanto in tanto e piugrave tardi potete caricare ognuno deglistati precedentemente salvati A differenza della maggioranza di videogiochiconservano in maniere intelligente lo spazio Tipicamente pochi file alla voltacambiano da una versione alla successiva Si puograve quindi risparmiare spaziosalvando le differenze invece di fare nuove copie complete

Controllo distribuito

Immaginate ora un videogioco difficilissimo Cosigrave difficile da terminare che moltiesperti giocatori da tutto il mondo decidono di collaborare e condividere le loropartite salvate per cercare di venirne a capo Gli Speedrun sono un esempio con-creto di questa pratica dei giocatori si specializzano ognuno a giocare un livellodello stesso gioco nel miglior modo possibile e collaborano cosigrave per ottenere deirisultati incredibili

Come costruireste un sistema che permetta loro di accedere facilmente ai sal-vataggi degli altri E che permetta di caricarne di nuovi

Nel passato ogni progetto usava un sistema di controllo di versione centraliz-zato Un server centrale unico da qualche parte manteneva tutte le partitesalvate Ogni giocatore conservava al massimo qualche salvataggio sul propriocomputer Quando un giocatore aveva intenzione di avanzare nel gioco scari-cava il salvataggio piugrave recente dal server centrale giocava per un porsquo salvava ericaricava sul server i progressi ottenuti cosigrave che ognuno potesse usufruirne

Ma che cosa succedeva se un giocatore per qualche ragione voleva accedere aduna vecchia partita salvata Forse percheacute la versione piugrave attuale si trovava inuno stato da cui non era piugrave possibile vincere il gioco percheacute qualcuno aveva di-menticato di raccogliere un oggetto al terzo livello e ora era necessario ritrovarelrsquoultima partita salvata in un momento in cui la partita egrave ancora completabile Omagari si desiderava paragonare due partite salvate precedentemente per vederequanto progresso avesse fatto un giocatore particolare

Potrebbero esserci molte ragioni per voler recuperare una vecchia versione mail risultato egrave sempre lo stesso era necessario chiederla al server centrale E piugravepartite salvate erano necessarie piugrave dati era necessario trasmettere

La nuova generazione di sistemi di controllo di versione di cui Git fa parte sonodetti sistemi distribuiti e possono essere pensati come una generalizzazione deisistemi centralizzati Quando i giocatori scaricano dal server centrale ricevono

4

tutti i giochi salvati non solo lrsquoultima Egrave come se fossero un mirror del servercentrale

Questa operazione iniziale di clonaggio puograve essere costosa soprattutto se crsquoegrave unalunga storia di salvataggi precedenti Ma a lungo termine egrave una strategia cheripaga Un beneficio immediato egrave che quando per qualche ragione si desideraun salvataggio precedente non egrave necessario comunicare con il server centrale

Una sciocca superstizione

Una credenza popolare vuole che i sistemi distribuiti non siano adatti a progettiche richiedono un deposito centrale ufficiale Niente potrebbe essere piugrave lontanodalla veritagrave Fotografare qualcuno non ne ruba lrsquoanima Similarmente clonareun deposito principale non ne diminuisce lrsquoimportanza

Una buona prima approssimazione egrave che tutto ciograve che puograve fare un sistema dicontrollo di versione centralizzato puograve essere fatto meglio da un sistema dis-tribuito ben concepito Le risorse di rete sono semplicemente piugrave costose che lerisorse locali Nonostante vedremo piugrave in lagrave che ci sono alcuni svantaggi asso-ciati agli approcci distribuiti ci sono meno probabilitagrave di fare paragoni sbagliatecon questa approssimazione

Un piccolo progetto potrebbe non necessitare di tutte le funzionalitagrave offerte daun tale sistema ma il fatto di usare un sistema difficilmente estensibile perprogetti piccoli egrave come usare il sistema di numerazione romano per calcoli connumeri piccoli

In aggiunta il vostro progetto potrebbe crescere al di lagrave delle vostre previsioniiniziali Usare Git dallrsquoinizio egrave come avere sempre con se un coltellino svizzeroanche se lo utilizzate primariamente per aprire delle bottiglie Quel giorno incui avrete disperatamente bisogno un cacciavite sarete felici di avere piugrave di unsemplice apribottiglie

Merge di conflitti

Per questo argomento la nostra analogia basata sui videogiochi inizia ad esseretirata per i capelli Ritorniamo quindi invece al caso della formattazione di undocumento

Immaginiamo che Alice inserisce una linea di codice allrsquoinizio di un file e Bobne aggiunge una alla fine della propria copia Entrambi caricano le loro modi-fiche nel deposito La maggior parte dei sistemi decideranno una linea drsquoazioneragionevole accettare e fondere (merge) entrambe le modifiche cosigrave che sia lemodifiche di Alice e Bob sono applicate

Ma supponiamo ora che Alice e Bob hanno fatto distinte modifiche alla stessalinea del documento Egrave impossibile procedere senza intervento umano La sec-

5

onda persona a caricare il file viene informata di un conflitto di merge e bisognascegliere una modifica piuttosto che un altra oppure riscrivere interamente lariga

Situazioni piugrave complesse possono presentarsi Sistemi di controllo di versioni sipossono occupare autonomamente dei casi piugrave semplici et lasciano i casi difficiliallrsquointervento umano Generalmente questo comportamento egrave configurabile

Trucchi di base

Piuttosto che immergerci nel mare di comandi di Git bagniamoci un porsquo i piedicon i seguenti esempi elementari Nonostante la loro semplicitagrave ognuno di loroegrave utile In effetti durante i miei mesi iniziali drsquoutilizzazione di Git non mi sonomai avventurato al di lagrave di del materiale in questo capitolo

Salvare lo stato corrente

Siete sul punto di fare qualcosa di drastico Prima di proseguire catturate lostato di tutti i file nella directory corrente

$ git init$ git add $ git commit -m Il mio primo backup

Qualora le cose dovessero andare per il peggio potrete sempre ripristinare laversione salvate

$ git reset --hard

Per salvare un nuovo state

$ git commit -a -m Un altro backup

Aggiungere rimuovere e rinominare file

Le istruzioni che abbiamo appena visto tengono traccia solo dei file che eranopresenti nel momento dellrsquoesecuzione di git add Ma se aggiungete nuovi file osottocartelle dovrete dirlo a Git

$ git add readmetxt Documentation

Analogamente se volete che Git ignori alcuni file

$ git rm kludgeh obsoletec$ git rm -r incriminatingevidence

6

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 3: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

GitHub Assembla e Bitbucket supportano deposito privati gli ultimi due sonogratuiti

Introduzione

Farograve uso di unrsquoanalogia per introdurre il controllo di versione Fate riferimentoalla pagina wikipedia sul controllo di versione per una spiegazione piugrave sobria

Il lavoro egrave gioco

Ho giocato ai videogiochi quasi tutta la mia vita Per contro ho iniziato adutilizzare sistemi di controllo di versione solo da adulto Sospetto di non essereil solo e il paragone tra i due puograve rendere alcuni concetti piugrave facile da spiegaree comprendere

Pensate alla scrittura di codice o documenti come ad un videogioco Non appenaavete fatto progressi sostanziali egrave desiderabile salvare il vostro lavoro Per fareciograve cliccate sul pulsante Salva del vostro fidato editor

Ma questo sovrascriveragrave la versione precedente del documento Egrave come queivecchi videogiochi in cui si poteva salvare la partita ma senza poter ritornare auno stato precedente del gioco Il che era un peccato percheacute il vostro salvataggioprecedente avrebbe potuto trovarsi ad un punto particolarmente divertente delgioco che avreste magari voluto rivisitare in futuro O ancora peggio se il vostrosalvataggio piugrave recente si fosse rivelato essere uno stato da cui egrave impossibilevincere il gioco obbligandovi a ricominciare la partita da zero

Controllo di versione

Quando modificate un documento di cui volete conservare le vecchie versionipotete Salvare comehellip sotto un nome di file diverso oppure copiare il file inunrsquoaltra cartella prima di salvarlo Potreste anche comprimere queste copiedel file se volete risparmiare spazio su disco Questa egrave una forma primitivae inefficiente forma di controllo di versione I videogiochi hanno miglioratoquesto punto molto tempo fa provvedendo in molti casi multiple possibilitagrave disalvataggio automaticamente ordinate temporalmente

Rendiamo il problema un porsquo piugrave complicato Immaginate di avere un un gruppodi file che vanno insieme come il codice sorgente di un progetto o i file per un sitoweb In questo caso se volete conservare una vecchia versione dovete archiviareuna directory intera Conservare diverse versioni a mano non egrave scomodo ediventa rapidamente impraticabile

3

Nel caso di alcuni videogiochi il salvataggio di una partita consiste effettiva-mente in una directory contenente diversi file Questi giochi nascondono questodettaglio al giocatore e presentano una comoda interfaccia per gestire le diverseversioni di tale cartella

I sistemi di controllo di versione non sono niente piugrave di questo Hanno tuttiuna comoda interfaccia per gestire una directory piena di file Potete salvare lostato della directory di tanto in tanto e piugrave tardi potete caricare ognuno deglistati precedentemente salvati A differenza della maggioranza di videogiochiconservano in maniere intelligente lo spazio Tipicamente pochi file alla voltacambiano da una versione alla successiva Si puograve quindi risparmiare spaziosalvando le differenze invece di fare nuove copie complete

Controllo distribuito

Immaginate ora un videogioco difficilissimo Cosigrave difficile da terminare che moltiesperti giocatori da tutto il mondo decidono di collaborare e condividere le loropartite salvate per cercare di venirne a capo Gli Speedrun sono un esempio con-creto di questa pratica dei giocatori si specializzano ognuno a giocare un livellodello stesso gioco nel miglior modo possibile e collaborano cosigrave per ottenere deirisultati incredibili

Come costruireste un sistema che permetta loro di accedere facilmente ai sal-vataggi degli altri E che permetta di caricarne di nuovi

Nel passato ogni progetto usava un sistema di controllo di versione centraliz-zato Un server centrale unico da qualche parte manteneva tutte le partitesalvate Ogni giocatore conservava al massimo qualche salvataggio sul propriocomputer Quando un giocatore aveva intenzione di avanzare nel gioco scari-cava il salvataggio piugrave recente dal server centrale giocava per un porsquo salvava ericaricava sul server i progressi ottenuti cosigrave che ognuno potesse usufruirne

Ma che cosa succedeva se un giocatore per qualche ragione voleva accedere aduna vecchia partita salvata Forse percheacute la versione piugrave attuale si trovava inuno stato da cui non era piugrave possibile vincere il gioco percheacute qualcuno aveva di-menticato di raccogliere un oggetto al terzo livello e ora era necessario ritrovarelrsquoultima partita salvata in un momento in cui la partita egrave ancora completabile Omagari si desiderava paragonare due partite salvate precedentemente per vederequanto progresso avesse fatto un giocatore particolare

Potrebbero esserci molte ragioni per voler recuperare una vecchia versione mail risultato egrave sempre lo stesso era necessario chiederla al server centrale E piugravepartite salvate erano necessarie piugrave dati era necessario trasmettere

La nuova generazione di sistemi di controllo di versione di cui Git fa parte sonodetti sistemi distribuiti e possono essere pensati come una generalizzazione deisistemi centralizzati Quando i giocatori scaricano dal server centrale ricevono

4

tutti i giochi salvati non solo lrsquoultima Egrave come se fossero un mirror del servercentrale

Questa operazione iniziale di clonaggio puograve essere costosa soprattutto se crsquoegrave unalunga storia di salvataggi precedenti Ma a lungo termine egrave una strategia cheripaga Un beneficio immediato egrave che quando per qualche ragione si desideraun salvataggio precedente non egrave necessario comunicare con il server centrale

Una sciocca superstizione

Una credenza popolare vuole che i sistemi distribuiti non siano adatti a progettiche richiedono un deposito centrale ufficiale Niente potrebbe essere piugrave lontanodalla veritagrave Fotografare qualcuno non ne ruba lrsquoanima Similarmente clonareun deposito principale non ne diminuisce lrsquoimportanza

Una buona prima approssimazione egrave che tutto ciograve che puograve fare un sistema dicontrollo di versione centralizzato puograve essere fatto meglio da un sistema dis-tribuito ben concepito Le risorse di rete sono semplicemente piugrave costose che lerisorse locali Nonostante vedremo piugrave in lagrave che ci sono alcuni svantaggi asso-ciati agli approcci distribuiti ci sono meno probabilitagrave di fare paragoni sbagliatecon questa approssimazione

Un piccolo progetto potrebbe non necessitare di tutte le funzionalitagrave offerte daun tale sistema ma il fatto di usare un sistema difficilmente estensibile perprogetti piccoli egrave come usare il sistema di numerazione romano per calcoli connumeri piccoli

In aggiunta il vostro progetto potrebbe crescere al di lagrave delle vostre previsioniiniziali Usare Git dallrsquoinizio egrave come avere sempre con se un coltellino svizzeroanche se lo utilizzate primariamente per aprire delle bottiglie Quel giorno incui avrete disperatamente bisogno un cacciavite sarete felici di avere piugrave di unsemplice apribottiglie

Merge di conflitti

Per questo argomento la nostra analogia basata sui videogiochi inizia ad esseretirata per i capelli Ritorniamo quindi invece al caso della formattazione di undocumento

Immaginiamo che Alice inserisce una linea di codice allrsquoinizio di un file e Bobne aggiunge una alla fine della propria copia Entrambi caricano le loro modi-fiche nel deposito La maggior parte dei sistemi decideranno una linea drsquoazioneragionevole accettare e fondere (merge) entrambe le modifiche cosigrave che sia lemodifiche di Alice e Bob sono applicate

Ma supponiamo ora che Alice e Bob hanno fatto distinte modifiche alla stessalinea del documento Egrave impossibile procedere senza intervento umano La sec-

5

onda persona a caricare il file viene informata di un conflitto di merge e bisognascegliere una modifica piuttosto che un altra oppure riscrivere interamente lariga

Situazioni piugrave complesse possono presentarsi Sistemi di controllo di versioni sipossono occupare autonomamente dei casi piugrave semplici et lasciano i casi difficiliallrsquointervento umano Generalmente questo comportamento egrave configurabile

Trucchi di base

Piuttosto che immergerci nel mare di comandi di Git bagniamoci un porsquo i piedicon i seguenti esempi elementari Nonostante la loro semplicitagrave ognuno di loroegrave utile In effetti durante i miei mesi iniziali drsquoutilizzazione di Git non mi sonomai avventurato al di lagrave di del materiale in questo capitolo

Salvare lo stato corrente

Siete sul punto di fare qualcosa di drastico Prima di proseguire catturate lostato di tutti i file nella directory corrente

$ git init$ git add $ git commit -m Il mio primo backup

Qualora le cose dovessero andare per il peggio potrete sempre ripristinare laversione salvate

$ git reset --hard

Per salvare un nuovo state

$ git commit -a -m Un altro backup

Aggiungere rimuovere e rinominare file

Le istruzioni che abbiamo appena visto tengono traccia solo dei file che eranopresenti nel momento dellrsquoesecuzione di git add Ma se aggiungete nuovi file osottocartelle dovrete dirlo a Git

$ git add readmetxt Documentation

Analogamente se volete che Git ignori alcuni file

$ git rm kludgeh obsoletec$ git rm -r incriminatingevidence

6

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 4: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Nel caso di alcuni videogiochi il salvataggio di una partita consiste effettiva-mente in una directory contenente diversi file Questi giochi nascondono questodettaglio al giocatore e presentano una comoda interfaccia per gestire le diverseversioni di tale cartella

I sistemi di controllo di versione non sono niente piugrave di questo Hanno tuttiuna comoda interfaccia per gestire una directory piena di file Potete salvare lostato della directory di tanto in tanto e piugrave tardi potete caricare ognuno deglistati precedentemente salvati A differenza della maggioranza di videogiochiconservano in maniere intelligente lo spazio Tipicamente pochi file alla voltacambiano da una versione alla successiva Si puograve quindi risparmiare spaziosalvando le differenze invece di fare nuove copie complete

Controllo distribuito

Immaginate ora un videogioco difficilissimo Cosigrave difficile da terminare che moltiesperti giocatori da tutto il mondo decidono di collaborare e condividere le loropartite salvate per cercare di venirne a capo Gli Speedrun sono un esempio con-creto di questa pratica dei giocatori si specializzano ognuno a giocare un livellodello stesso gioco nel miglior modo possibile e collaborano cosigrave per ottenere deirisultati incredibili

Come costruireste un sistema che permetta loro di accedere facilmente ai sal-vataggi degli altri E che permetta di caricarne di nuovi

Nel passato ogni progetto usava un sistema di controllo di versione centraliz-zato Un server centrale unico da qualche parte manteneva tutte le partitesalvate Ogni giocatore conservava al massimo qualche salvataggio sul propriocomputer Quando un giocatore aveva intenzione di avanzare nel gioco scari-cava il salvataggio piugrave recente dal server centrale giocava per un porsquo salvava ericaricava sul server i progressi ottenuti cosigrave che ognuno potesse usufruirne

Ma che cosa succedeva se un giocatore per qualche ragione voleva accedere aduna vecchia partita salvata Forse percheacute la versione piugrave attuale si trovava inuno stato da cui non era piugrave possibile vincere il gioco percheacute qualcuno aveva di-menticato di raccogliere un oggetto al terzo livello e ora era necessario ritrovarelrsquoultima partita salvata in un momento in cui la partita egrave ancora completabile Omagari si desiderava paragonare due partite salvate precedentemente per vederequanto progresso avesse fatto un giocatore particolare

Potrebbero esserci molte ragioni per voler recuperare una vecchia versione mail risultato egrave sempre lo stesso era necessario chiederla al server centrale E piugravepartite salvate erano necessarie piugrave dati era necessario trasmettere

La nuova generazione di sistemi di controllo di versione di cui Git fa parte sonodetti sistemi distribuiti e possono essere pensati come una generalizzazione deisistemi centralizzati Quando i giocatori scaricano dal server centrale ricevono

4

tutti i giochi salvati non solo lrsquoultima Egrave come se fossero un mirror del servercentrale

Questa operazione iniziale di clonaggio puograve essere costosa soprattutto se crsquoegrave unalunga storia di salvataggi precedenti Ma a lungo termine egrave una strategia cheripaga Un beneficio immediato egrave che quando per qualche ragione si desideraun salvataggio precedente non egrave necessario comunicare con il server centrale

Una sciocca superstizione

Una credenza popolare vuole che i sistemi distribuiti non siano adatti a progettiche richiedono un deposito centrale ufficiale Niente potrebbe essere piugrave lontanodalla veritagrave Fotografare qualcuno non ne ruba lrsquoanima Similarmente clonareun deposito principale non ne diminuisce lrsquoimportanza

Una buona prima approssimazione egrave che tutto ciograve che puograve fare un sistema dicontrollo di versione centralizzato puograve essere fatto meglio da un sistema dis-tribuito ben concepito Le risorse di rete sono semplicemente piugrave costose che lerisorse locali Nonostante vedremo piugrave in lagrave che ci sono alcuni svantaggi asso-ciati agli approcci distribuiti ci sono meno probabilitagrave di fare paragoni sbagliatecon questa approssimazione

Un piccolo progetto potrebbe non necessitare di tutte le funzionalitagrave offerte daun tale sistema ma il fatto di usare un sistema difficilmente estensibile perprogetti piccoli egrave come usare il sistema di numerazione romano per calcoli connumeri piccoli

In aggiunta il vostro progetto potrebbe crescere al di lagrave delle vostre previsioniiniziali Usare Git dallrsquoinizio egrave come avere sempre con se un coltellino svizzeroanche se lo utilizzate primariamente per aprire delle bottiglie Quel giorno incui avrete disperatamente bisogno un cacciavite sarete felici di avere piugrave di unsemplice apribottiglie

Merge di conflitti

Per questo argomento la nostra analogia basata sui videogiochi inizia ad esseretirata per i capelli Ritorniamo quindi invece al caso della formattazione di undocumento

Immaginiamo che Alice inserisce una linea di codice allrsquoinizio di un file e Bobne aggiunge una alla fine della propria copia Entrambi caricano le loro modi-fiche nel deposito La maggior parte dei sistemi decideranno una linea drsquoazioneragionevole accettare e fondere (merge) entrambe le modifiche cosigrave che sia lemodifiche di Alice e Bob sono applicate

Ma supponiamo ora che Alice e Bob hanno fatto distinte modifiche alla stessalinea del documento Egrave impossibile procedere senza intervento umano La sec-

5

onda persona a caricare il file viene informata di un conflitto di merge e bisognascegliere una modifica piuttosto che un altra oppure riscrivere interamente lariga

Situazioni piugrave complesse possono presentarsi Sistemi di controllo di versioni sipossono occupare autonomamente dei casi piugrave semplici et lasciano i casi difficiliallrsquointervento umano Generalmente questo comportamento egrave configurabile

Trucchi di base

Piuttosto che immergerci nel mare di comandi di Git bagniamoci un porsquo i piedicon i seguenti esempi elementari Nonostante la loro semplicitagrave ognuno di loroegrave utile In effetti durante i miei mesi iniziali drsquoutilizzazione di Git non mi sonomai avventurato al di lagrave di del materiale in questo capitolo

Salvare lo stato corrente

Siete sul punto di fare qualcosa di drastico Prima di proseguire catturate lostato di tutti i file nella directory corrente

$ git init$ git add $ git commit -m Il mio primo backup

Qualora le cose dovessero andare per il peggio potrete sempre ripristinare laversione salvate

$ git reset --hard

Per salvare un nuovo state

$ git commit -a -m Un altro backup

Aggiungere rimuovere e rinominare file

Le istruzioni che abbiamo appena visto tengono traccia solo dei file che eranopresenti nel momento dellrsquoesecuzione di git add Ma se aggiungete nuovi file osottocartelle dovrete dirlo a Git

$ git add readmetxt Documentation

Analogamente se volete che Git ignori alcuni file

$ git rm kludgeh obsoletec$ git rm -r incriminatingevidence

6

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 5: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

tutti i giochi salvati non solo lrsquoultima Egrave come se fossero un mirror del servercentrale

Questa operazione iniziale di clonaggio puograve essere costosa soprattutto se crsquoegrave unalunga storia di salvataggi precedenti Ma a lungo termine egrave una strategia cheripaga Un beneficio immediato egrave che quando per qualche ragione si desideraun salvataggio precedente non egrave necessario comunicare con il server centrale

Una sciocca superstizione

Una credenza popolare vuole che i sistemi distribuiti non siano adatti a progettiche richiedono un deposito centrale ufficiale Niente potrebbe essere piugrave lontanodalla veritagrave Fotografare qualcuno non ne ruba lrsquoanima Similarmente clonareun deposito principale non ne diminuisce lrsquoimportanza

Una buona prima approssimazione egrave che tutto ciograve che puograve fare un sistema dicontrollo di versione centralizzato puograve essere fatto meglio da un sistema dis-tribuito ben concepito Le risorse di rete sono semplicemente piugrave costose che lerisorse locali Nonostante vedremo piugrave in lagrave che ci sono alcuni svantaggi asso-ciati agli approcci distribuiti ci sono meno probabilitagrave di fare paragoni sbagliatecon questa approssimazione

Un piccolo progetto potrebbe non necessitare di tutte le funzionalitagrave offerte daun tale sistema ma il fatto di usare un sistema difficilmente estensibile perprogetti piccoli egrave come usare il sistema di numerazione romano per calcoli connumeri piccoli

In aggiunta il vostro progetto potrebbe crescere al di lagrave delle vostre previsioniiniziali Usare Git dallrsquoinizio egrave come avere sempre con se un coltellino svizzeroanche se lo utilizzate primariamente per aprire delle bottiglie Quel giorno incui avrete disperatamente bisogno un cacciavite sarete felici di avere piugrave di unsemplice apribottiglie

Merge di conflitti

Per questo argomento la nostra analogia basata sui videogiochi inizia ad esseretirata per i capelli Ritorniamo quindi invece al caso della formattazione di undocumento

Immaginiamo che Alice inserisce una linea di codice allrsquoinizio di un file e Bobne aggiunge una alla fine della propria copia Entrambi caricano le loro modi-fiche nel deposito La maggior parte dei sistemi decideranno una linea drsquoazioneragionevole accettare e fondere (merge) entrambe le modifiche cosigrave che sia lemodifiche di Alice e Bob sono applicate

Ma supponiamo ora che Alice e Bob hanno fatto distinte modifiche alla stessalinea del documento Egrave impossibile procedere senza intervento umano La sec-

5

onda persona a caricare il file viene informata di un conflitto di merge e bisognascegliere una modifica piuttosto che un altra oppure riscrivere interamente lariga

Situazioni piugrave complesse possono presentarsi Sistemi di controllo di versioni sipossono occupare autonomamente dei casi piugrave semplici et lasciano i casi difficiliallrsquointervento umano Generalmente questo comportamento egrave configurabile

Trucchi di base

Piuttosto che immergerci nel mare di comandi di Git bagniamoci un porsquo i piedicon i seguenti esempi elementari Nonostante la loro semplicitagrave ognuno di loroegrave utile In effetti durante i miei mesi iniziali drsquoutilizzazione di Git non mi sonomai avventurato al di lagrave di del materiale in questo capitolo

Salvare lo stato corrente

Siete sul punto di fare qualcosa di drastico Prima di proseguire catturate lostato di tutti i file nella directory corrente

$ git init$ git add $ git commit -m Il mio primo backup

Qualora le cose dovessero andare per il peggio potrete sempre ripristinare laversione salvate

$ git reset --hard

Per salvare un nuovo state

$ git commit -a -m Un altro backup

Aggiungere rimuovere e rinominare file

Le istruzioni che abbiamo appena visto tengono traccia solo dei file che eranopresenti nel momento dellrsquoesecuzione di git add Ma se aggiungete nuovi file osottocartelle dovrete dirlo a Git

$ git add readmetxt Documentation

Analogamente se volete che Git ignori alcuni file

$ git rm kludgeh obsoletec$ git rm -r incriminatingevidence

6

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 6: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

onda persona a caricare il file viene informata di un conflitto di merge e bisognascegliere una modifica piuttosto che un altra oppure riscrivere interamente lariga

Situazioni piugrave complesse possono presentarsi Sistemi di controllo di versioni sipossono occupare autonomamente dei casi piugrave semplici et lasciano i casi difficiliallrsquointervento umano Generalmente questo comportamento egrave configurabile

Trucchi di base

Piuttosto che immergerci nel mare di comandi di Git bagniamoci un porsquo i piedicon i seguenti esempi elementari Nonostante la loro semplicitagrave ognuno di loroegrave utile In effetti durante i miei mesi iniziali drsquoutilizzazione di Git non mi sonomai avventurato al di lagrave di del materiale in questo capitolo

Salvare lo stato corrente

Siete sul punto di fare qualcosa di drastico Prima di proseguire catturate lostato di tutti i file nella directory corrente

$ git init$ git add $ git commit -m Il mio primo backup

Qualora le cose dovessero andare per il peggio potrete sempre ripristinare laversione salvate

$ git reset --hard

Per salvare un nuovo state

$ git commit -a -m Un altro backup

Aggiungere rimuovere e rinominare file

Le istruzioni che abbiamo appena visto tengono traccia solo dei file che eranopresenti nel momento dellrsquoesecuzione di git add Ma se aggiungete nuovi file osottocartelle dovrete dirlo a Git

$ git add readmetxt Documentation

Analogamente se volete che Git ignori alcuni file

$ git rm kludgeh obsoletec$ git rm -r incriminatingevidence

6

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 7: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Git rimuoveragrave questi file per voi se non lrsquoavete ancora fatto

Un file puograve essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovonome Egrave anche possibile usare la scorciatoia git mv che segue la stessa sintassidel comando mv Ad esempio

$ git mv bugc featurec

AnnullareRipristino avanzati

A volte puograve capitare che vogliate solamente ritornare indietro e dimenticare lemodifiche effettuate dopo un certo punto percheacute sono tutte sbagliate In quelcaso

$ git log

vi nostra una lista dei commit piugrave recenti accompagnati dal loro codice SHA1

commit 766f9881690d240ba334153047649b8b8f11c664Author Bob ltbobexamplecomgtDate Tue Mar 14 015926 2000 -0800

Sostituzione di prinf() con write()

commit 82f5ea346a2e651544956a8653c0f58dc151275cAuthor Alice ltaliceexamplecomgtDate Thu Jan 1 000000 1970 +0000

Commit iniziale

I primi caratteri del codice SHA1 sono sufficienti per specificare un commitalternativamente copiate e incollate lrsquointero codice SHA1 Digitate

$ git reset --hard 766f

per reinstaurare lo stato corrispondente al commit corrente e permanentementecancellare i commit piugrave recenti

Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno statoprecedente In questo caso eseguite

$ git checkout 82f5

Questo vi riporta indietro nel tempo preservando i commit piugrave recenti Bisognaperograve sapere che come in ogni viaggio nel tempo in un film di fantascienza seora modificate e sottomettete un commit vi ritroverete in una realtagrave alternativapercheacute avrete fatto delle azioni differenti rispetto alla realtagrave originaria

Questa realtagrave parallela viene chiamata ramificazione o branch et vi dirograve di piugravein proposito in seguito Per ora egrave abbastanza ricordare che

7

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 8: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ git checkout master

vi riporta al presente Inoltre per evitare che Git si lamenti ricordatevi di fareun commit o un reset delle vostre modifiche prima di fare un checkout

Per riprendere lrsquoanalogia con i videogiochi digitate

bull git reset --hard carica un vecchio salvataggio e cancella tutte lepartite salvate piugrave recenti di quella appena caricata

bull git checkout carica una vecchia partita ma se ci giocate lo statodella partita saragrave diverso da quello dei salvataggi successivi che avete fatoinizialmente Da ora in poi ogni volta che salvate una partita finirete inun branch separata che rappresenta la realtagrave parallela in cui siete entratiCi occuperemo di questo piugrave tardi

Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli allafine del seguente comando

$ git checkout 82f5 unfile un-altrofile

Fate perograve attenzione questa forma di checkout puograve sovrascrivere dei file senzaavvertimenti Per evitare incidenti fate un commit prima di eseguire un co-mando di checkout specialmente se siete alle prime armi con Git In generaleogni volta che non siete sicuri delle conseguenze di comando che sia di Git ono eseguite prima git commit -a

Non vi piace copiare e incollare codice hash Allora utilizzate

$ git checkout Il mio primo b

per saltare direttamente al commit che inizia con quel messaggio Potete anchechiedere ad esempio il quintultimo stato salvato

$ git checkout master~5

Annullare (revert)

In una corte di giustizia certi avvenimenti possono essere stralciati dal processoverbale Analogamente potete selezionare degli specifici commit da annullare

$ git commit -a$ git revert 1b6d

annulla solo lrsquoultimo commit con il dato codice hash Il revert viene registratocome un nuovo commit fatto che potrete verificare eseguendo un git log

Generare un diario delle modifiche (changelog)

Certi progetti richiedono un changelog Createlo digitando

8

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 9: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ git log gt ChangeLog

Scaricare dei files

Fate una copia di un progetto gestito da Git digitando

$ git clone gitserverpercorsoversofiles

Ad esempio per ottenere tutti i file che ho usato per creare questo sito

$ git clone gitgitorczgitmagicgit

Avremo molto da dire a proposito del comando clone tra poco

Lrsquoultima versione

Se avete giagrave scaricato una copia di un progetto usando git clone potete ag-giornarla allrsquoultima versione con

$ git pull

Pubblicazione istantanea

Immaginate di aver scritto uno script che volete condividere con altri Potrestesemplicemente dire loro di scaricarlo dal vostro computer ma le fanno mentrestate migliorando lo script o sperimentando con delle modifiche potrebberofinire nei guai Naturalmente questo tipo di situazioni sono la ragione per cuiesistono i cicli di rilascio Gli sviluppatori possono lavorare frequentemente adun progetto ma rilasciano il codice solo quando hanno lrsquoimpressione che siapresentabile

Per fare questo con Git nella cartella che contiene lo script eseguite

$ git init$ git add $ git commit -m Prima versione

In seguito dite agli utenti di eseguire

$ git clone ilvostrocomputerpercorsoversoloscript

per scaricare il vostro script Questo assume che tutti abbiamo accesso ssh alvostro computer Se non fosse il caso eseguite git daemon e dite ai vostriutenti di eseguire invece

$ git clone gitilvostrocomputerpercorsoversoloscript

A partire da questo momento ogni volta che il vostro script egrave pronto per essererilasciato eseguite

9

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 10: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ git commit -a -m Nuova versione

e i vostri utenti potranno aggiornare la loro versione andando nella cartella checontiene lo script e digitando

$ git pull

I vostri utenti non si ritroveranno mai piugrave con una versione del vostro script chenon volete che vedano

Che cosa ho fatto

Ritroverete le modifiche fatte dallrsquoultimo commit con

$ git diff

Oppure quelle a partire da ieri con

$ git diff yesterday

O tra una versione specifica e due versioni fa

$ git diff 1b6d master~2

In ogni caso il risultato egrave una patch che puograve essere applicata con git applyPotete anche provare

$ git whatchanged --since=2 weeks ago

Spesso esamino invece la storia dei commits con qgit per via della sua sfolgo-rante interfaccia oppure tig unrsquointerfaccia in modalitagrave testo che funziona beneanche con le connessioni piugrave lente Alternativamente installate un server weblanciate git instaweb e lanciate il vostro browser

Esercizio

Siano A B C D quattro commit successivi dove B egrave identico a A conlrsquoeccezione che alcuni file sono stati rimossi Vogliamo rimettere i file in DCome possiamo fare

Ci sono almeno tre soluzioni Assumiamo che siamo in D

1 La differenza tra A e B sono i file rimossi Possiamo creare una patch cherappresenti la differenza e applicarla

$ git diff B A | git apply

2 Visto che i files in questioni sono presenti in A possiamo recuperarli

$ git checkout A fooc barh

10

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 11: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

3 Possiamo anche pensare al passo da A a B come ad una modifica chevogliamo annullare

$ git revert B

Quel egrave la scelta migliore Quella che preferite Egrave facile ottenere quello chevolete con Git e spesso ci sono diversi modi di ottenerlo

Cloniamo

In vecchi sistemi di controllo di versione lrsquooperazione standard per otteneredei file era il checkout Ottenete cosigrave un insieme di file corrispondenti a unparticolare stato precedentemente salvato

In Git e altri sistemi distribuiti di controllo versione lrsquooperazione standard egrave ilclonaggio Per ottenere dei file si crea un clone di tutto il deposito In altreparole diventate praticamente un mirror del server centrale Tutto ciograve che puogravefare il deposito centrale potete farlo anche voi

Sincronizzazione tra computers

Posso tollerare lrsquoidea di creare degli archivi tar o di utilizzare rsync per backupdi base Ma a volte lavoro sul mio laptop altre volte sul mio desktop e puogravedarsi che nel frattempo le due macchine non si siano parlate

Inizializzate un deposito Git e fate un commit dei vostri file su una macchinaPoi sullrsquoaltra eseguite

$ git clone altrocomputerpercorsoversoilfile

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

$ git commit -a$ git pull altrocomputerpercorsoversoilfile HEAD

trasferiragrave lo stato dei file sullrsquoaltro computer aggiornando quello su cui statelavorando Se avete recentemente fatto delle modifiche conflittuali dello stessofile Git ve lo segnaleragrave e dovrete ripetere nuovamente il commit dopo che avreterisolto il conflitto

Controllo classico di file sorgente

Inizializzate il deposito Git dei vostri file

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

11

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 12: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

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

$ mkdir projgit$ cd projgit$ git init --bare$ touch projgitgit-daemon-export-ok

Se necessario lanciate il daemon

$ git daemon --detach potrebbe giagrave essere in esecuzione

Per servizi di hosting Git seguite le istruzioni per il setup del deposito Git cheinizialmente saragrave vuoto Tipicamente bisogneragrave riempire un formulario in unapagina web

Trasferite il vostro progetto sul server centrale con

$ git push gitservercentralepercorsofinoaprojgit HEAD

Per ottenere i file sorgente uno sviluppatore deve eseguire

$ git clone gitservercentralepercorsofinoaprojgit

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 pushfallisce et lo sviluppatore deve aggiornarsi allrsquoultima versione risolvere eventualiconflitti e provare di nuovo

Percheacute i comandi pull e push precedenti funzionino bisogna avere accesso SSHComunque chiunque puograve vedere il codice sorgente digitando

$ git clone gitservercentralepercorsofinoaprojgit

Il protocollo nativo git egrave come lrsquoHTTP non crsquoegrave nessuna autenticazione cosigraveche tutti possono ottenere il progetto Quindi per default push egrave proibito conprotocollo git

12

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 13: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

File sorgente segreti

Per un progetto chiuso omettete il comando touch e assicuratevi di mai creareun file chiamato git-daemon-export-ok Il deposito in questo caso non potragravepiugrave essere ottenuto con il protocollo git solo chi ha accesso SSH potragrave vederloSe tutti i vostri deposito sono chiusi lanciare il daemon git non egrave necessariopercheacute la comunicazione avviene via SSH

Depositi nudi

Un deposito nudo (bare repository) si chiama cosigrave percheacute non possiede unacartella di lavoro contiene solo i file che sono solitamente nascosti nella sotto-cartella git In altre parole mantiene unicamente la storia del progetto e enon conserva nessuna versione

Un deposito nudo gioca un ruolo simile a quello di un server principale in unsistema di controllo di versione centralizzato egrave dove egrave localizzato il vostro pro-getto Altri sviluppatori clonano il nostro progetto da ligrave e vi trasferiscono gliultimi modifiche ufficiali Tipicamente si trova su un server che non fa altro chedistribuire dati Lo sviluppo avviene nei cloni cosigrave che il deposito principalenon ha bisogno di una cartella di lavoro

Molti comandi git non funzionano per depositi nudi a meno che la variabileglobale GIT_DIR non viene definita con il percorso al deposito o si utilizzalrsquoopzione --bare

Push vs pull

Percheacute abbiamo introdotto il comando push invece di affidarci al piugrave familiarecomando pull Prima di tutto il comando pull non funziona con depositinudi in questo caso bisogna invece usare fetch un comando che discuteremopiugrave tardi Ma anche se avessimo un deposito normale sul server centrale usarepull sarebbe sarebbe scomodo Bisognerebbe per prima cosa connettersi alserver e poi dare come argomento a pull lrsquoindirizzo della macchina dalla qualevogliamo ottenere le modifiche I firewalls potrebbero interferire nel processo ecosa faremmo se non avessimo nemmeno accesso shell al server

In ogni caso questo caso a parte vi scoraggiamo lrsquouso di push per via dellaconfusione che potrebbe generare quando la destinazione ha una cartella dilavoro

In conclusione mentre state imparando ad usare Git usate push solo se ladestinazione egrave un deposito nudo altrimenti usate pull

13

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 14: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Fare il forking di un progetto

Stufi del modo in cui un progetto egrave amministrato Pensate che potreste fare unlavoro migliore In questo caso dal vostro server eseguite

$ git clone gitserverprincipalepercorsoversoifiles

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 pro-getto ha moti sviluppatori non crsquoegrave bisogno di fare niente Ogni clone del vostrocodice egrave effettivamente un backup Non solo dello stato corrente ma dellrsquointerastoria del vostro progetto Grazie al hashing crittografico se qualcuno dovesseavere un clone corrotto saragrave individuato non appena si connetteragrave agli altri

Se il vostro progetto non egrave molto popolare trovate il piugrave alto numero possibiledi server che possano ospitare dei cloni

Il vero paranoico dovrebbe anche sempre annotarsi lrsquoultimo codice SHA1dellrsquoHEAD di 20 bytes in un posto sicuro Deve essere sicuro non privatoAd esempio pubblicarlo in un giornale funzionerebbe bene visto che sarebbedifficile realizzare un attacco modificando tutte le copie del giornale

Multi-tasking alla velocitagrave della luce

Immaginiamo di voler lavorare simultaneamente su diverse funzionalitagrave Inquesto caso fate un commit del progetto e eseguite

$ git clone unanuovacartella

Grazie ai collegamenti fisici i cloni locali richiedono meno tempo e spazio che ibackup usuali

Potete ora lavorare simultaneamente su due funzionalitagrave indipendentementeAd esempio potete modificare un clone mentre lrsquoaltro sta compilando Ad ognimodo potete validare con commit le vostre modifiche e importare con pull icambiamenti dagli altri cloni

$ git pull ilmioaltroclone HEAD

14

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 15: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Controllo di versione da battaglia

State lavorando ad un progetto che usa qualche altro sistema di controllo diversione e vi manca disperatamente Git In tal caso inizializzate un depositoGit nella vostra cartella di lavoro

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

poi clonatelo

$ git clone unanuvacartella

Ora navigate alla nuova cartella e lavorate da qua utilizzando Git come vo-lete Di tanto in tanto quando volete sincronizzarvi con gli altri recatevi nellacartella originale sincronizzate utilizzando lrsquoaltro sistema di controllo di ges-tione 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 drsquoaltrocanto dallrsquoaltro sistema di controllo di versione La nuova cartella contiene ifile con i vostri cambiamenti Lanciate qualsiasi comando dellrsquoaltro sistema dicontrollo di gestione sia necessario per inviarli al deposito centrale

Subversion che egrave forse il migliore sistema di gestione di versione centralizzatoegrave utilizzato da innumerevoli progetti Il comando git svn automatizza la proce-dura precedente per i depositi Subversion e puograve anche essere usato per esportareun progetto Git in un deposito Subversion

Mercurial

Mercurial egrave un sistema di controllo di versione che puograve funzionare in tandemcon Git in modo quasi trasparente Con il plugin hg-git un utente di Mercurialpuograve senza svantaggi inviare a (push) e ottenere (pull) da un deposito Git

Scaricate il plugin hg-git con Git

$ git clone gitgithubcomschaconhg-gitgit

o Mercurial

$ hg clone httpbitbucketorgdurin42hg-git

15

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 16: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Sfortunatamente non sembra ci sia un plugin analogo per Git Per questa ra-gione mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositiprincipali Nel caso di un progetto Mercurial di solito un volontario mantienein parallelo un deposito Git che accomoda utenti Git mentre grazie al pluginhg-git un progetto Git accomoda automaticamente utenti Mercurial

Nonostante il plugin puograve convertire un deposito Mercurial in uno Git trasferen-dolo in un deposito vuoto questo egrave piugrave facile con lo script hg-fast-exportshottenibile da

$ git clone gitrepoorczfast-exportgit

Per fare una conversione in una nuovo cartella eseguite

$ git init$ hg-fast-exportsh -r depothg

dopo aver aggiunto lo script al vostro $PATH

Bazaar

Menzioniamo brevemente Bazaar percheacute egrave il sistema di controllo di versionedistribuito gratuito piugrave popolare dopo Git e Mercurial

Bazaar ha il vantaggio del senno di poi visto che egrave relativamente giovane i suoidisegnatori hanno potuto imparare dagli errori commessi nel passato e evitaregli scogli storici Inoltre i suoi sviluppatori sono attenti a questioni come laportabilitagrave e lrsquointeroperabilitagrave con altri sistemi di controllo di versione

Un plugin chiamato bzr-git permette agli utilizzatori di Bazaar di lavorare condepositi Git in una certa misura Il programma tailor converte depositi Bazaarin depositi Git e puograve farlo in maniera incrementale mentre bzr-fast-exportegrave fatto per le conversioni uniche

Percheacute utilizzo Git

Ho originariamente scelto Git percheacute avevo sentito che era in grado di gestirelrsquoinimmaginabilmente ingestibile sorgente del kernel Linux Non ho mai sentitola necessitagrave di cambiare Git mi ha servito un servizio impeccabile e non sonomai stato colto alla sprovvista dai suoi limiti Siccome utilizzo primariamenteLinux i problemi che appaiono sulle altre piattaforme non mi concernono

In piugrave preferisco programmi in C e scripts in bash rispetto agli eseguibili tipogli scripts Python ci sono meno dipendenze e sono dipendente allrsquoalta velocitagravedi esecuzione

Ho riflettuto a come migliorare Git arrivando fino al punto di scrivere la miapropria versione ma solo come un esercizio accademico Anche se avessi com-

16

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 17: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

pletato il mio progetto sarei rimasto a Git comunque visto che i vantaggisarebbero stati minimi per giustificare lrsquoutilizzazione di un sistema solitario

Naturalmente i vostri bisogni e richieste probabilmente differiscono dai miei equindi potreste trovarvi meglio con un altro sistema Nonostante ciograve non potetesbagliarvi scegliendo Git

La stregoneria delle branch

Le funzioni di merge e di ramificazione (o branch) sono le migliori rdquokiller featuresrdquodi Git

Problema Fattori esterni conducono inevitabilmente a cambiamenti di con-testo Un grave bug si manifesta inaspettatamente nella versione di releaseLa scadenza per una particolare funzionalitagrave viene anticipata Uno sviluppa-tore che doveva collaborare con voi su una parte delicata di un progetto non egravepiugrave disponibile In ogni caso dovete bruscamente smettere quello che stavatefacendo per concentrarvi su un compito completamente diverso

Interrompere il flusso dei vostri pensieri puograve essere controproducente e piugravescomodo egrave il cambiamento di contesto piugrave grande egrave lo svantaggio Con unsistema di controllo di versione centralizzato bisognerebbe scaricare una nuovacopia del lavoro dal server centrale Un sistema decentralizzato egrave migliore percheacutepermette di clonare localmente la versione che si vuole

Ma clonare richiede comunque di copiare unrsquointera cartella di lavoro in aggiuntaallrsquointera storia fino al punto voluto Anche se Git riduce i costi tramite lacondivisione di file e gli hard link i file di progetto stessi devono essere ricreatiinteramente nella nuova cartella di lavoro

Soluzione Git ha un metodo migliore per queste situazioni che egrave molto miglioreed efficiente in termini di spazio che il clonaggio il comando git branch

Grazie a questa parola magica i file nella directory si trasformano immediata-mente da una versione a unrsquoaltra Questa trasformazione puograve fare molto di piugraveche portarvi avanti e indietro nella storia del progetto I vostri file possonotrasformarsi dallrsquoultima release alla versione corrente di sviluppo alla versionedi un vostro collega ecc

Boss key

Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ldquoboss keyldquo)che nasconde immediatamente la schermata coprendola con qualcosa come unatabella di calcolo In questo modo se il vostro capo entra nel vostro ufficiomentre state giocando potete nasconderlo rapidamente

In una cartella vuota eseguite

17

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 18: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ echo Sono piugrave intelligente che il mio capo gt myfiletxt$ git init$ git add $ git commit -m Commit iniziale

Avete appena creato un deposito Git che gestisce un file di testo che contieneun certo messaggio Adesso digitate

$ git checkout -b capo niente sembra essere cambiato dopo questo$ echo Il mio capo egrave piugrave intelligente di me gt myfiletxt$ git commit -a -m Un altro commit

Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo incommit le modifiche Ma questo non egrave che unrsquoillusione Ora digitate

$ git checkout master Passa alla versione originale del file

e voilagrave Il file di testo egrave ritornato alla versione originale E se il vostro capo simettesse a curiosare in questa cartella eseguite

$ git checkout capo Passa alla versione accettabile dal capo

Potete passare da una versione allrsquoaltra in qualsiasi momento e mettere incommit le vostre modifiche per ognuna indipendentemente

Lavoro temporaneo

Diciamo che state lavorando ad una funzionalitagrave e per qualche ragione doveteritornare a tre versioni precedenti e temporaneamente aggiungere qualcheistruzione per vedere come funziona qualcosa Fate

$ git commit -a$ git checkout HEAD~3

Ora potete aggiungere codice temporaneo ovunque vogliate Potete addiritturafare un commit dei cambiamenti Quando avete finito eseguite

$ git checkout master

per ritornare al vostro lavoro originario Ricordatevi che i cambiamenti nonsottomessi ad un commit andranno persi

Che fare se nonostante tutto voleste salvare questi cambiamenti temporaneiFacile

$ git checkout -b temporaneo

e fate un commit prima di ritornare alla branch master Qualora voleste ri-tornare ai cambiamenti temporanei eseguite semplicemente

$ git checkout temporaneo

18

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 19: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Abbiamo giagrave parlato del comando checkout in un capitolo precedente mentrediscutevamo il caricamento di vecchi stati Ne parleremo ancora piugrave avantiPer ora ci basta sapere questo i file vengono cambiati allo stato richiesto mabisogna lasciare la branch master A partire da questo momento tutti i commitporteranno i vostri file su una strada diversa che potragrave essere nominata piugraveavanti

In altre parole dopo un checkout verso uno stato precedente Git ci posizionaautomaticamente in una nuova branch anonima che potragrave essere nominata esalvata con git checkout -b

Correzioni rapide

Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiestodi lasciar perdere tutto per correggere un bug appena scoperto nella versionelsquo1b6dhelliplsquo

$ git commit -a$ git checkout -b correzioni 1b6d

Poi quando avete corretto il bug eseguite

$ git commit -a -m Bug corretto$ git checkout master

per riprendere il lavoro originario Potete anche fare un merge delle nuovecorrezioni del bug

$ git merge correzioni

Merge

Con alcuni sistemi di controllo di versione creare delle branch egrave molto facile mafare un merge egrave difficile Com Git fare un merge egrave cosigrave facile che potreste anchenon accorgervi che lo state facendo

Infatti abbiamo giagrave incontrato il merge molto tempo fa Il comando pull recu-pera (fetch) una serie di versioni e le incorpora (merge) nella branch correnteSe non ci sono cambiamenti locali il merge egrave un semplicemente salto in avanti(un fast forward) un caso degenere simile a ottenere la versione piugrave recente inun sistema di controllo di versione centralizzato Ma se ci sono cambiamentilocali Git faragrave automaticamente un merge riportando tutti i conflitti

Normalmente una versione ha una sola versione genitore vale a dire la versioneprecedente Fare un merge di brach produce una versione con almeno duegenitori Questo solleva la seguente domanda a quale versione corrispondeHEAD~10 Visto che una versione puograve avere parecchi genitori quali dobbiamoseguire

19

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 20: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Si dagrave il caso che questa notazione si riferisce sempre al primo genitore Questoegrave desiderabile percheacute la versione corrente diventa il primo genitore in un mergee spesso si egrave piugrave interessati ai cambiamenti fatti nella branch corrente piuttostoche ai cambiamenti integrati dalle altre branch

Potete fare riferimento ad un genitore specifico con un accento circonflesso Adesempio per vedere il log del secondo genitore

$ git log HEAD^2

Potete omettere il numero per il primo genitore Ad esempio per vedere ledifferenze con il primo genitore

$ git diff HEAD^

Potete combinare questa notazione con le altre Ad esempio

$ git checkout 1b6d^^2~10 -b ancient

inizia la nuova branch ldquoancientrdquo nello stato corrispondente a 10 versioni prece-denti il secondo genitore del primo genitore del commit il cui nome inizia con1b6d

Flusso di lavoro ininterrotto

Spesso in un progetto ldquohardwarerdquo la seconda tappa deve aspettare il comple-tamento della prima Unrsquoautomobile in riparazione deve rimanere bloccata ingarage fino allrsquoarrivo di una particolare parte di ricambio Un prototipo deveaspettare la fabbricazione di un processore prima che la costruzione possa con-tinuare

I progetti software possono essere simili La seconda parte di una nuova funzion-alitagrave puograve dover aspettare fino a che la prima parte venga completata e testataAlcuni progetti richiedono che il vostro codice sia rivisto prima di essere ac-cettato Siete quindi obbligati ad aspettare lrsquoapprovazione della prima parteprima di iniziare la seconda

Grazie alla facilitagrave con cui si creano delle branch e si effettua un merge sipossono piegare le regole e lavorare sulla parte II prima che la parte I sia uffi-cialmente pronta Supponiamo che avete fatto il commit della parte I e lrsquoavetesottomessa per approvazione Diciamo che siete nella branch master Createallora una nuova branch cosigrave

$ git checkout -b part2

In seguito lavorate sulla parte II fate il commit dei cambiamenti quando nec-essario Errare egrave umano e spesso vorrete tornare indietro e aggiustare qualcosanella parte I Se siete fortunati o molto bravi potete saltare questo passaggio

$ git checkout master Ritorno alla parte 1$ correzione_problemi

20

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 21: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ git commit -a Commit delle correzioni$ git checkout part2 Ritorno alla parte 2$ git merge master Merge delle correzioni

Finalmente la parte I egrave approvata

$ git checkout master Ritorno alla parte I$ distribuzione files Distribuzione in tutto il mondo$ git merge part2 Merge della parte II$ git branch -d part2 Eliminazione della branch part2

In questo momento siete di nuovo nella branch master con la parte II nellavostra cartella di lavoro

Egrave facile estendere questo trucco a qualsiasi numero di parti Egrave anche facile crearedelle branch retroattivamente supponiamo che ad un certo punto vi accorgeteche avreste dovuto creare una branch 7 commit fa Digitate allora

$ git branch -m master part2 Rinomina la branch master con il nome part2$ git branch master HEAD~7 Crea una nuova branch master 7 commits nel passato

La branch master contiene ora solo la parte I e la branch part2 contiene il restoNoi siamo in questa seconda branch abbiamo creato master senza spostarvicipercheacute vogliamo continuare a lavorare su part2 Questo egrave inusuale Fino adora spostavamo in una branch non appena la creavamo come in

$ git checkout HEAD~7 -b master Crea una branch e vi si sposta

Riorganizzare un pasticcio

Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branchVolete che i vostri lavori in corso siano accessibili solo a voi stessi e voleteche altri possano vedere le vostre versioni solo quando sono ben organizzateCominciamo creando due branch

$ git branch ordine Crea una branch per commit organizzati$ git checkout -b pasticcio Crea e si sposta in una branch in cui lavorare

In seguito lavorate su tutto quello che volete correggere bugs aggiungere fun-zionalitagrave aggiungere codice temporaneo e cosigrave via facendo commit quandonecessario Poi

$ git checkout ordine$ git cherry-pick pasticcio^^

applica le modifiche della versione progenitore della corrente versione ldquopasticciordquoalla versione ldquoordinerdquo Con i cherry-pick appropriati potete costruire una branchche contiene solo il codice permanente e che raggruppa tutti i commit collegati

21

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 22: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Gestione di branch

Per ottenere una lista di tutte le branch digitate

$ git branch

Per default iniziate nella branch chiamata ldquomasterrdquo Alcuni raccomandano dilasciare la branch ldquomasterrdquo intatta e di creare nuove branch per le proprie mod-ifiche

Le opzioni -d e -m permettono di cancellare e spostare (rinominare) le branchPer piugrave informazioni vedete git help branch

La branch ldquomasterrdquo egrave una convenzione utile Gli altri possono assumere che ilvostro deposito ha una branch con quel nome e che questa contiene la versioneufficiale del vostro progetto Nonostante sia possibile rinominare o cancellare labranch ldquomasterrdquo puograve essere utile rispettare le tradizioni

Branch temporanee

Dopo un certo tempo drsquoutilizzo potreste accorgervi che create frequentementebranch temporanee per ragioni simili vi servono solamente per salvare lo statocorrente cosigrave da rapidamente saltare ad uno stato precedente per correggere unbug prioritario o qualcosa di simile

Egrave analogo a cambiare temporaneamente canale televisivo per vedere cosrsquoaltrocrsquoegrave alla TV Ma invece di premere un paio di bottoni dovete creare spostarvifare merge e cancellare branch temporanee Fortunatamente Git possiede unascorciatoia che egrave altrettanto pratica che il telecomando del vostro televisore

$ git stash

Questo salva lo stato corrente in un posto temporaneo (uno stash) e ristabiliscelo stato precedente La vostra cartella di lavoro appare esattamente comrsquoeraprima di fare le modifiche e potete correggere bugs incorporare cambiamentidel deposito centrale (pull) e cosigrave via Quando volete ritornare allo stato cor-rispondente al vostro stash eseguite

$ git stash apply Potreste dover risolvere qualche conflitto

Potete avere stash multipli e manipolarli in modi diversi Vedere git helpstash per avere piugrave informazioni Come avrete indovinato Git mantiene dellebranch dietro le quinte per realizzare questi trucchi magici

Lavorate come volete

Potreste chiedervi se vale la pena usare delle branch Dopotutto creare deicloni egrave un processo altrettanto rapido e potete passare da uno allrsquoaltro con un

22

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 23: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

semplice cd invece che gli esoterici comandi di Git

Consideriamo un browser web Percheacute supportare tabs multiple oltre a finestremultiple Percheacute permettere entrambi accomoda una gamma drsquoutilizzazione piugraveampia Ad alcuni utenti piace avere una sola finestra e usare tabs per multiplepagine web Altri insistono con lrsquoestremo opposto multiple finestre senza tabsAltri ancora preferiscono qualcosa a metagrave

Le branch sono come delle tabs per la vostra cartella di lavoro e i cloni sonocome nuove finestre del vostro browser Queste operazioni sono tutte veloci elocali Quindi percheacute non sperimentare per trovare la combinazione che piugrave visi addice Con Git potete lavorare esattamente come volete

Lezioni di storia

Una delle conseguenze della natura distribuita di Git egrave che il corso storico puograveessere modificato facilmente Ma se alterate il passato fate attenzione riscrivetesolo le parti di storia che riguardano solo voi Nello stesso modo in cui nazionidibattono le responsabilitagrave di atrocitagrave se qualcun altro ha un clone la cui storiadifferisce dalla vostra avrete problemi a riconciliare le vostre differenze

Certi sviluppatori insistono che la storia debba essere considerata immutabileinclusi i difetti Altri pensano invece che le strutture storiche debbano essererese presentabili prima di essere presentate pubblicamente Git egrave compatibilecon entrambi i punti di vista Come con lrsquouso di clone branch e merge riscriverela storia egrave semplicemente un altra capacitagrave che vi permette Git Sta a voi farnebuon uso

Mi correggo

Avete appena fatto un commit ma ora vi accorgete che avreste voluto scrivereun messaggio diverso Allora eseguite

$ git commit --amend

per modificare lrsquoultimo messaggio Vi siete accorti di aver dimenticato di ag-giungere un file Allora eseguite git add per aggiungerlo e eseguite il comandoprecedente

Volete aggiungere qualche modifica supplementare nellrsquoultimo commit Allorafatele e eseguite

$ git commit --amend -a

23

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 24: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

hellip e ancora di piugrave

Supponiamo che il problema precedente egrave dieci volte peggio Dopo una lungaseduta avete fatto parecchi commit Ma non siete soddisfatto da come sono or-ganizzati e alcuni messaggi di commit potrebbero essere riscritti meglio Alloradigitate

$ git rebase -i HEAD~10

e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito Eccoun piccolo estratto come esempio

pick 5c6eb73 Added repoorcz linkpick a311a64 Reordered analogies in Work How You Wantpick 100834f Added push target to Makefile

I commit piugrave vecchi precedono quelli piugrave recenti in questa lista a differenza delcomando log Qua 5c6eb73 egrave il commit piugrave vecchio e 100834f egrave il piugrave recenteIn seguito

bull Rimuovete un commit cancellando la sua linea Egrave simile al comando revertma egrave come se il commit non fosse mai esistito

bull Cambiate lrsquoordine dei commit cambiando lrsquoordine delle linee

bull Sostituite pick con

ndash edit per marcare il commit per essere modificato

ndash reword per modificare il messaggio nel log

ndash squash per fare un merge del commit con quello precedente

ndash fixup per fare un merge di questo commit con quello precedente erimuovere il messaggio nel log

Ad esempio possiamo sostituire il secondo pick con squash

pick 5c6eb73 Added repoorcz linksquash a311a64 Reordered analogies in Work How ou Wantpick 100834f Added push target to Makefile

Dopo aver salvato ed essere usciti dal file Git fa un merge di a311a64 in 5c6eb73Quindi squash fa un merge combinando le versioni nella versione precedente

Git quindi combina i loro messaggi e li presenta per eventuali modifiche Ilcomando fixup salta questo passo il messaggio log a cui viene applicato ilcomando viene semplicemente scartato

Se avete marcato un commit con edit Git vi riporta nel passato al commitpiugrave vecchio Potete correggere il vecchio commit come descritto nella sezioneprecedente e anche creare nuovi commit nella posizione corrente Non appenasiete soddisfatto con le rettifiche ritornate in avanti nel tempo eseguendo

24

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 25: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ git rebase --continue

Git ripercorre i commit fino al prossimo edit o fino al presente se non ne rimanenessuno

Potete anche abbandonare il vostro tentativo di cambiare la storia con rebasenel modo seguente

$ git rebase --abort

Quindi fate dei commit subito e spesso potrete mettere tutto in ordine piugravetardi con rebse

E cambiamenti locali per finire

State lavorando ad un progetto attivo Fate alcuni commit locali e poi visincronizzate con il deposito ufficiale con un merge Questo ciclo si ripete qualchevolta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti neldeposito centrale con push

Ma a questo punto la storia del vostro clone Git locale egrave un confuso garbugliodi modifiche vostre e ufficiali Preferireste vedere tutti i vostri cambiamenti inuna sezione contigua seguita dai cambiamenti ufficiali

Questo egrave un lavoro per git rebase come descritto precedentemente In molticasi potete usare la flag --onto per evitare interazioni

Leggete git help rebase per degli esempi dettagliati di questo fantastico co-mando Potete scindere dei commit Potete anche riarrangiare delle branch diun deposito

State attenti rebase egrave un comando potente In casi complessi fate prima unbackup con git clone

Riscrivere la storia

Occasionalmente crsquoegrave bisogno di fare delle modifiche equivalenti a cancellare conpersone da una foto ufficiale cancellandole dalla storia in stile Stalinista Peresempio supponiamo che avete intenzione di pubblicare un progetto ma chequesto include un file che per qualche ragione volete tenere privato Diciamoad esempio che ho scritto il mio numero di carta di credito in un file che hoaggiunto per sbaglio al progetto Cancellare il file non egrave abbastanza visto chesi puograve ancora recuperare accedendo ai vecchi commit Quello che bisogna fareegrave rimuovere il file da tutti i commit

$ git filter-branch --tree-filter rm filesegreto HEAD

25

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 26: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Nella documentazione in git help filter-branch viene discusso questo esempioe dagrave anche un metodo piugrave rapido In generale filter-branch vi permette dimodificare intere sezioni della storia con un singolo comando

In seguito la cartella gitrefsoriginal conterragrave lo stato del vostro depositoprima dellrsquooperazione Verificate che il comando filter-branch abbia fatto quelloche desiderate e cancellate questa cartella se volete eseguire ulteriori comandifilter-branch

Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se aveteintenzione di interagire con loro piugrave tardi

Fare la storia

Volete far migrare un progetto verso Git Se egrave gestito con uno dei sistemi piugravediffusi egrave molto probabile che qualcuno abbia giagrave scritto uno script per esportarelrsquointera storia verso Git

Altrimenti documentatevi sul comando git fast-import che legge un file ditesto in un formato specifico per creare una storia Git a partire dal nulla Tipi-camente uno script che utilizza questo comando egrave uno script usa-e-getta scrittorapidamente e eseguito una volta sola per far migrare il progetto

Come esempio incollate il testo seguente in un file temporaneo chiamatotmphistory

commit refsheadsmastercommitter Alice ltaliceexamplecomgt Thu 01 Jan 1970 000000 +0000data ltltEOTCommit inizialeEOT

M 100644 inline hellocdata ltltEOTinclude ltstdiohgt

int main() printf(Hello worldn)return 0

EOT

commit refsheadsmastercommitter Bob ltbobexamplecomgt Tue 14 Mar 2000 015926 -0800data ltltEOTRemplacement de printf() par write()

26

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 27: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

EOT

M 100644 inline hellocdata ltltEOTinclude ltunistdhgt

int main() write(1 Hello worldn 14)return 0

EOT

Poi create un deposito Git a partire da questo file temporaneo eseguendo

$ mkdir project cd project git init$ git fast-import --date-format=rfc2822 lt tmphistory

Potete fare il checkout dellrsquoultima versione di questo progetto con

$ git checkout master

Il comando git fast-export puograve convertire qualsiasi deposito Git nel formatogit fast-import che vi permette di studiare come funzionano gli script diesportazione e vi permette anche di convertire un deposito in un formato facil-mente leggibile Questi comandi permettono anche di inviare un deposito at-traverso canali che accettano solo formato testo

Dovrsquoegrave che ho sbagliato

Avete appena scoperto un bug in una funzionalitagrave del vostro programma chesiete sicuri funzionasse qualche mese fa Argh Da dove viene questo bug Sesolo aveste testato questa funzionalitagrave durante lo sviluppo

Ma egrave troppo tardi Drsquoaltra parte a condizione di aver fatto dei commit abbas-tanza spesso Git puograve identificare il problema

$ git bisect start$ git bisect bad HEAD$ git bisect good 1b6d

Git estrae uno stato a metagrave strada di queste due versioni (HEAD e 1b6d)Testate la funzionalitagrave e se ancora non funziona

$ git bisect bad

Altrimenti rimpiazzate rdquobadrdquo con rdquogoodrdquo Git vi trasporta a un nuovo stato ametagrave strada tra le versioni rdquobuonerdquo e quelle rdquocattiverdquo riducendo cosigrave le possi-bilitagrave Dopo qualche iterazione questa ricerca binaria vi condurragrave al commitche ha causato problemi Una volta che la vostra ricerca egrave finita ritornate allostato originario digitando

27

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 28: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

$ git bisect reset

Invece di testare ogni cambiamento a mano automatizzate la ricerca scrivendo

$ git bisect run my_script

Git usa il valore di ritorno dello script my_script che avete passato per deciderese un cambiamento egrave buono o cattivo my_script deve terminare con il valore 0quando una versione egrave ok 125 quando deve essere ignorata o un valore tra 1 e127 se ha un bug Un valore di ritorno negativo abbandona il comando bisect

Ma potete fare molto di piugrave la pagina di help spiega come visualizzare lebisezioni esaminare o rivedere il log di bisect e eliminare noti cambiamentiinnocui per accelerare la ricerca

Chi egrave che ha sbagliato

Come in molti altri sistemi di controllo di versione Git ha un comando perassegnare una colpa

$ git blame bugc

Questo comando annota ogni linea del file mostrando chi lrsquoha cambiata perultimo e quando A differenza di molti altri sistemi di controllo di versionequesta operazione egrave eseguita off-line leggendo solo da disco locale

Esperienza personale

In un sistema di controllo di versione centralizzato le modifiche della storia sonounrsquooperazione difficile che egrave solo disponibile agli amministratori Creare unclone una branch e fare un merge sono delle operazioni impossibili senza unaconnessione di rete La stessa cosa vale per operazioni di base come ispezionarela storia o fare il commit di un cambiamento In alcuni sistemi egrave necessariauna connessione di rete anche solo per vedere le proprie modifiche o per aprireun file con diritto di modifica

Sistemi centralizzati precludono il lavoro off-line e necessitano infrastrutture direte piugrave ampie allrsquoaumentare del numero di sviluppatori Ancora piugrave importanteegrave il fatto che le operazioni sono a volte cosigrave lente da scoraggiare lrsquouso di alcunefunzioni avanzate a meno che non siano assolutamente necessarie In casi es-tremi questo puograve valere addirittura per comandi di base Quando gli utentidevono eseguire comandi lenti la produttivitagrave viene compromessa per via dellecontinue interruzioni del flusso di lavoro

Ho sperimentato questi fenomeni personalmente Git egrave stato il primo sistemadi controllo di versione che ho utilizzato Mi sono velocemente abituato alsuo uso dando per scontate molte funzionalitagrave Assumevo semplicemente che

28

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 29: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

altri sistemi fossero simili scegliere un sistema di controllo di versione non misembrava diverso da scegliere un editor di testo o un navigatore web

Sono rimasto molto sorpreso quando piugrave tardi sono obbligato ad utilizzare unsistema centralizzato Una connessione internet instabile ha poca importanzacon Git ma rende lo sviluppo quasi impossibile quando il sistema esige che siatanto affidabile quanto il disco locale Inoltre mi sono trovato ad evitare lrsquousodi alcuni comandi per via delle latenze che comportavano fatto che finalmentemi impediva di seguire il metodo di lavoro abituale

Quando dovevo eseguire un comando lento le interruzioni influivano molto neg-ativamente sulla mia concentrazione Durante lrsquoattesa della fine delle comuni-cazioni col server facevo qualcosrsquoaltro per passare il tempo come ad esempiocontrollare le email o scrivere della documentazione Quando ritornavo al lavoroiniziale il comando aveva terminato da tempo e mi ritrovare a dover cercare diricordare che cosa stessi facendo Gli esseri umani non sono bravi a passare daun contesto allrsquoaltro

Crsquoerano anche interessanti effetti di tragedia dei beni comuni prevedendo con-gestioni di rete alcuni utenti consumavano piugrave banda di rete che necessario pereffettuare operazioni il cui scopo era di ridurre le loro attese future Questi sforzicombinati risultavano ad aumentare ulteriormente le congestioni incoraggiandoa consumare ancora piugrave larghezza di banda per cercare di evitare latenze semprepiugrave lunghe

Git multi-giocatore

Inizialmente usavo Git per progetti privati dove ero lrsquounico sviluppatore Tra icomandi legati alla natura distribuita di Git avevo bisogno solamente di pulle clone cosigrave da tenere lo stesso progetto in posti diversi

Piugrave tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche didiversi contributori Ho dovuto imparare a gestire progetti con multipli svilup-patori da tutto il mondo Fortunatamente questo egrave il punto forte di Git eprobabilmente addirittura la sua ragion drsquoessere

Chi sono

Ogni commit ha il nome e lrsquoindirizzo e-mail di un autore i quali sono mostratidal comando git log Per default Git utilizza i valori di sistemamastery perdefinire questi campi Per configurarli esplicitamente digitate

$ git config --global username John Doe$ git config --global useremail johndoeexamplecom

29

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 30: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Omettete lrsquoopzione --global per configurare questi valori solo per il depositocorrente

Git via SSH e HTTP

Supponiamo che avete un accesso SSH a un server web sul quale Git non egrave perograveinstallato Anche se meno efficiente rispetto al suo protocollo nativo Git puogravecomunicare via HTTP

Scaricate compilate e installate Git sul vostro conto e create un deposito nellavostra cartella web

$ GIT_DIR=projgit git init$ cd projgit$ git --bare update-server-info$ cp hookspost-updatesample hookspost-update

Con versioni meno recenti di Git il comando di copia non funziona e doveteeseguire

$ chmod a+x hookspost-update

Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone

$ git push webserverpathtoprojgit master

e chiunque puograve ottenere il vostro progetto con

$ git clone httpwebserverprojgit

Git tramite qualsiasi canale

Volete sincronizzare dei depositi senza server o addirittura senza connessionedi rete Avete bisogno di improvvisare durante unrsquoemergenza abbiamo giagravevisto chegit fast-export e git fast-import possono convertire depositi in unsemplice file Possiamo quindi inviare questo tipo di file avanti e indietro pertrasportare depositi Git attraverso un qualsiasi canale Ma uno strumento piugraveefficace egrave il comando git bundle

Il mittente crea un pacchetto detto bundle

$ git bundle create qualche_file HEAD

poi trasmette il bundle qualche_file al destinatario attraverso qualsiasimetodo email chiave USB stampa e riconoscimento caratteri lettura di bitvia telefono segnali di funo ecc Il destinatario puograve recuperare i commit dalbundle digitando

$ git pull qualche_file

30

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 31: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Il destinatario puograve effettuare ciograve anche in deposito interamente vuoto Malgradola sua dimensione qualche_file contiene lrsquointero deposito Git originario

Nel caso di progetti grandi riducete gli sprechi includendo nel bundle solo icambiamenti che mancano nellrsquoaltro deposito Per esempio supponiamo cheil commit ldquo1b6dhelliprdquo egrave il commit piugrave recente che egrave condiviso dai due depositiPossiamo ora eseguire

$ git bundle create qualche_file HEAD ^1b6d

Se fatta di frequente potremmo facilmente dimenticare quale commit egrave statomandato per ultimo La pagina drsquoaiuto suggerisce di utilizzare delle tag perrisolvere questo problema In pratica appena dopo aver inviato il bundle digi-tate

$ git tag -f ultimo_bundle HEAD

e create un nuovo bundle con

$ git bundle create nuovo_bundle HEAD ^ultimo_bundle

Le patch la moneta di scambio globale

Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che pos-sono essere facilmente comprensibili sia per computer che umani Egrave quello chele rende interessanti Potete mandare una patch per email ad altri sviluppatoriindipendentemente dal sistema di controllo di versione che utilizzano A partiredal momento che possono leggere le loro email possono vedere le vostre modi-fiche Similarmente da parte vostra non avete bisogno che di un indirizzo emailnon crsquoegrave neanche bisogno di avere un deposito Git online

Ricordatevi dal primo capitolo il comando

$ git diff 1b6d gt mypatch

produce una patch che puograve essere incollata in unrsquoemail per discussioni In undeposito Git eseguite

$ git apply lt mypatch

per applicare la patch

In un contesto piugrave formale quando egrave il nome e magari la firma dellrsquoautoredevono essere presenti generate le patch a partire da un certo punto digitando

$ git format-patch 1b6d

I file risultanti possono essere passati a git-send-email o inviati a manoPotete anche specificare un intervallo tra due commit

$ git format-patch 1b6dHEAD^^

31

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 32: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Dalla parte del destinatario salvate lrsquoemail in un file (diciamo emailtxt) e poidigitate

$ git am lt emailtxt

Questo applica le patch ricevute e crea inoltre un commit includendo infor-mazioni come il nome dellrsquoautore

Se utilizzate un client email in un navigatore web potreste dover cercare il mododi vedere il messaggio nel suo formato rdquorawrdquo originario prima di salvare la patchcome file

Ci sono delle leggere differenze nel caso di client email che si basano sul formatombox ma se utilizzate uno di questi siete probabilmente il tipo di persona cheriesce a risolverle senza bisogno di leggere questo tutorial

Ci dispiace abbiamo cambiato indirizzo

Dopo aver conato un deposito lrsquoesecuzione di git push o git pull faragrave au-tomaticamente riferimento allrsquoURL del deposito drsquoorigine Come fa Git Ilsegreto risiede nelle opzioni di configurazione create durante la clonazione Di-amoci unrsquoocchiata

$ git config --list

Lrsquoopzione remoteoriginurl determina lrsquoURL della sorgente ldquooriginrdquo egrave lrsquoaliasdel deposito drsquoorigina Come per la convenzione di nominare ldquomasterrdquo la branchprincipale possiamo cambiare o cancellare questo alias ma non crsquoegrave normalmentenessuna ragione per farlo

Se lrsquoindirizzo del deposito originario cambia potete modificare il suo URL con

$ git config remoteoriginurl gitnewurlprojgit

Lrsquoopzione branchmastermerge specifica la branch di default utilizzata dalcomando git pull Al momento della clonazione iniziale il nome scelto egrave quellodella branch corrente del deposito originario Anche se lrsquoHEAD del depositodrsquoorigine egrave spostato verso unrsquoaltra branch il comando pull continueragrave a seguirefedelmente la branch iniziale

Questrsquoopzione si applicheragrave unicamente al deposito usato nel clonazione inizialecioegrave quello salvato nellrsquoopzione branchmasterremote Se effettuiamo un pullda un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo

$ git pull gitexamplecomothergit master

Questo spiega tra lrsquoaltro come mai alcuni dei precedenti esempi di push e pullnon avevano nessun argomento

32

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 33: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Branch remote

Quando cloniamo un deposito cloniamo anche tutte le sue branch Magari nonve ne siete accorti percheacute Git le nascondei dovete chiedere esplicitamente divederle Questo impedisce alle branch del deposito remoto drsquointerferire con levostre branch e rende lrsquouso di Git piugrave facile per i novizi

Per ottenere una lista delle branch remote eseguite

$ git branch -r

Dovreste ottenere qualcosa come

originHEADoriginmasteroriginexperimental

Questi sono le branch e lrsquoHEAD del deposito remoto e possono essere usati innormali comandi Git Supponiamo per esempio di aver fatto molti commit eche ora volete paragonare le differenze con lrsquoultima versione ottenibile con fetchPotreste cercare nel log il codice SHA1 appropriato ma egrave molto piugrave semplicescrivere

$ git diff originHEAD

Oppure potete anche vedere che cosa sta succedendo nella branch ldquoexperimen-talrsquorsquo

$ git log originexperimental

Depositi remoti multipli

Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto eche vogliate tenerli drsquoocchio entrambi Possiamo seguire piugrave depositi allo stessotempo con

$ git remote add altro gitexamplecomun_depositogit$ git pull altro una_branch

Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamoavere facile accesso a tutte le branch di tutti i depositi

$ git diff originexperimental^ altrouna_branch~5

Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificareil nostro lavoro I altre parole vogliamo esaminare le loro branch senza che leloro modifiche invadano la nostra cartella di lavoro In questo caso invece difare un pull eseguite

$ git fetch Fetch dal deposito dorigine il default$ git fetch altro Fetch dal secondo programmatore

33

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 34: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Questo fa un fetch solamente delle storie Nonostante la cartella di lavoro rimaneintatta possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandiGit percheacute ora abbiamo una copia locale

Ricordatevi che dietro le quinte un pull egrave semplicemente un fetch seguito daun merge Normalmente facciamo un pull percheacute vogliamo ottenere un mergedelle ultime modifiche dopo aver fatto un fetch La situazione precedente egrave unanotevole eccezione

Guardate git help remote per sapere come eliminare depositi remoti ignoraredelle branch e ancora di piugrave

Le mie preferenze

Per i miei progetti mi piace che i contributori preparino depositi dai quali possofare in pull Alcuni servizi di host Git permettono di creare i vostri cloni di unprogetto con il click di un bottone

Dopo aver fatto il fetch di una serie di modifiche utilizzo i comandi Git pernavigare e esaminare queste modifiche che idealmente saranno ben organizzatee descritte Faccio il merge dei miei cambiamenti e forse qualche modifica inpiugrave Una volta soddisfatto faccio un push verso il deposito principale

Nonostante non riceva molto spesso dei contributi credo che questo approccioscali bene In proposito vi consiglio di guardare questo post di Linus Torvalds

Restare nel mondo di Git egrave un porsquo piugrave pratiche che usare file di patch visto chemi risparmia di doverli convertire in commit Git Inoltre Git gestisce diretta-mente dettagli come salvare il nome e lrsquoindirizzo email dellrsquoautore cosigrave come ladata e lrsquoora e chiede anche allrsquoautore di descrivere i cambiamenti fatti

Padroneggiare Git

A questo punto dovreste essere capaci di navigare la guida git help e dicapire quasi tutto (a condizione ovviamente di capire lrsquoinglese) Nonostante ciograveritrovare il comando esatto richiesto per risolvere un particolare problema puograveessere tedioso Magari posso aiutarvi a risparmiare un porsquo di tempo qua sottotrovate qualcuna delle ricette di cui ho avuto bisogno in passato

Pubblicazione di codice sorgente

Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubbli-care Per creare un archivio in formato tar del codice sorgente utilizzo

$ git archive --format=tar --prefix=proj-123 HEAD

34

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 35: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Commit dei cambiamenti

Dire a Git quando avete aggiunto cancellato o rinominato dei file puograve esserefastidioso per certi progetti Invece potete eseguire

$ git add $ git add -u

Git cercheragrave i file della cartella corrente e gestiragrave tutti i dettagli automatica-mente Invece del secondo comando add eseguite git commit -a se voleteanche fare un commit Guardate git help ignore per sapere come specificarei file che devono essere ignorati

Potete anche effettuare tutti i passi precedenti in un colpo solo con

$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove

Le opzioni -z e -0 permettono di evitare effetti collaterali dovuti a file il cuinome contiene strani caratteri Visto che questo comando aggiunge anche fileche sono ignorati potreste voler usare le opzioni -x o -X

Il mio commit egrave troppo grande

Vi siete trascurati da un porsquo di tempo di fare dei commit Avete scritto codicefuriosamente dimenticandovi di controllo di versione Avete implementato unaserie di cambiamenti indipendenti percheacute egrave il vostro stile di lavoro

Non crsquoegrave problema Eseguite

$ git add -p

Per ognuna delle modifiche che avete fatto Git vi mostreragrave la parte di codiceche egrave stata cambiata e vi domanderagrave se dovragrave fare parte del prossimo commitRispondete con rdquoyrdquo (sigrave) o con rdquonrdquo (no) Avete anche altre opzioni come dipostporre la decisione digitate rdquordquo per saperne di piugrave

Una volta soddisfatti eseguite

$ git commit

per fare un commit che comprende esattamente le modifiche selezionate (le mod-ifiche nellarea di staging vedere dopo) Assicuratevi di omettere lrsquoopzione-a altrimenti Git faragrave un commit che includeragrave tutte le vostre modifiche

Che fare se avete modificato molti file in posti diversi Verificare ogni cambia-mento uno alla volta diviene allora rapidamente frustrante e noioso In questocaso usate git add -i la cui interfaccia egrave meno intuitiva ma piugrave flessibile Conqualche tasto potete aggiungere o togliere piugrave file alla volta dallrsquoarea di stagingoppure anche rivedere e selezionare cambiamenti in file particolari Altrimentipotete anche eseguire git commit --interactive che effettueragrave automatica-mente un commit quando avrete finito

35

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 36: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Lrsquoindice lrsquoarea di staging

Fino ad ora abbiamo evitato il famoso indice di Git ma adesso dobbiamo par-larne per capire meglio il paragrafo precedente Lrsquoindice egrave unrsquoarea temporaneadi cosiddetto staging Git trasferisce raramente dati direttamente dal vostroprogetto alla sua storia Invece Git scrive prima i dati nellrsquoindice e poi copiatutti i dati dellrsquoindice nella loro destinazione finale

Un commit -a egrave ad esempio in realtagrave un processo a due fasi La prima fasestabilisce unrsquoistantanea (un cosiddetto snapshot) dello stato corrente di ognifile in gestione e la ripone nellrsquoindice La seconda fase salva permanentementequesto snapshot Effettuare un commit senza lrsquoopzione -a esegue solamente laseconda fase e ha quindi solo senso solo a seguito di un comando che modificalrsquoindice come ad esempio git add

Normalmente possiamo ignorare lrsquoindice e comportandoci effettivamente comese se stessimo scambiando dati direttamente nella storia In altri casi comequello precedente vogliamo un controllo piugrave fine e manipoliamo quindi lrsquoindiceInseriamo nellrsquoindice uno snapshot di alcuni ma non tutti i cambiamenti e poisalviamo permanentemente questi snapshot accuratamente costruiti

Non perdete la rdquotestardquo

La tag HEAD egrave come un cursore che normalmente punta allrsquoultimo commitavanzando con ogni commit Alcuni comandi di Git permettono di muoverlaAd esempio

$ git reset HEAD~3

sposta HEAD tre commit indietro Da qua via tutti i comandi Git agiscono comese non aveste fatto quegli ultimi tre commit mentre i vostri file rimangono nellostato presente Vedere la pagina di help per qualche applicazione interessante

Ma come fare per ritornare al futuro I commit passati non sanno niente delfuturo

Se conoscete il codice SHA1 dellrsquoHEAD originario (diciamo 1b6dhellip) fate allora

$ git reset 1b6d

Ma come fare se non lrsquoavete memorizzato Non crsquoegrave problema per comandi diquesto genere Git salva lrsquoHEAD originario in una tag chiamata ORIG_HEADe potete quindi ritornare al futuro sani e salvi con

$ git reset ORIG_HEAD

36

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 37: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Cacciatore di rdquotesterdquo

ORIG_HEAD puograve non essere abbastanza Diciamo che vi siete appena accortidi un monumentale errore e dovete ritornare ad un vecchio commit in una branchdimenticata da lungo tempo

Per default Git conserva un commit per almeno due settimane anche se gliavete ordinato di distruggere la branch lo conteneva La parte difficile egrave trovareil codice hash appropriato Potete sempre far scorrere tutti i codici hash ilgitobjects e trovare quello che cercate per tentativi Crsquoegrave perograve un modomolto piugrave facile

Git registra ogni codice hash che incontra in gitlogs La sottocartella refscontiene la storia dellrsquoattivitagrave di tutte le branch mentre il file HEAD mostra tuttii codici hash che HEAD ha assunto Questrsquoultimo puograve usato per trovare commitdi una branch che egrave stata accidentalmente cancellata

Il comando reflog provvede unrsquointerfaccia intuitiva per gestire questi file di logProvate a eseguire

$ git reflog

Invece di copiare e incollare codici hash dal reflog provate

$ git checkout 10 minutes ago

O date unrsquoocchiata al quintultimo commit visitato con

$ git checkout 5

Vedete la sezione ldquoSpecifying Revisionsrdquo di git help rev-parse per avere piugravedettagli

Potreste voler configurare un periodo piugrave lungo per la ritenzione dei commit dacancellare Ad esempio

$ git config gcpruneexpire 30 days

significa che un commit cancellato saragrave perso permanentemente eliminato solo30 giorni piugrave tardi quando git gc saragrave eseguito

Potete anche voler disabilitare lrsquoesecuzione automatica di git gc

$ git config gcauto 0

nel qual caso commit verranno solo effettivamente eliminati allrsquoesecuzione man-uale di git gc

Costruire sopra Git

In vero stile UNIX il design di Git ne permette lrsquoutilizzo come componente abasso livello di altri programmi come interfacce grafiche e web interfacce di

37

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 38: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

linea alternative strumenti di gestione di patch programmi di importazione econversione ecc Infatti alcuni comandi Git sono loro stessi script che fannoaffidamento ad altri comandi di base Con un porsquo di ritocchi potete voi stessipersonalizzare Git in base alle vostre preferenze

Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare lefunzioni che utilizzate di frequente

$ git config --global aliasco checkout$ git config --global --get-regexp alias mostra gli alias correntialiasco checkout$ git co foo equivalente a git checkout foo

Un altro trucco consiste nellrsquointegrare il nome della branch corrente nella vostralinea di comando o nel titolo della finestra Lrsquoinvocazione di

$ git symbolic-ref HEAD

mostra il nome completo della branch corrente In pratica vorrete probabil-mente togliere rdquorefsheadsrdquo e ignorare gli errori

$ git symbolic-ref HEAD 2gt devnull | cut -b 12-

La sottocartella contrib egrave uno scrigno di utili strumenti basati suGit Un giorno alcuni di questi potrebbero essere promossi al rangodi comandi ufficiali Su Debian e Ubuntu questa cartella si trova inusrsharedocgit-corecontrib

Uno dei piugrave popolari tra questi script si trova in workdirgit-new-workdirGrazie ad un link simbolico intelligente questo script crea una nuova cartelladi lavoro la cui storia egrave condivisa con il deposito originario

$ git-new-workdir undepositoesistente nuovacartella

La nuova cartella e i suoi file possono essere visti come dei cloni salvo per ilfatto che la storia egrave condivisa e quindi i rimane automaticamente sincronizzataNon crsquoegrave quindi nessun bisogno di fare merge push o pull

Acrobazie audaci

Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmentedei dati Ma se sapete cosa state facendo potete escludere le misure di sicurezzadei comandi piugrave comuni

Checkout Checkout non funziona in caso di Modifiche non integrate con com-mit Per distruggere i vostri cambiamenti ed effettuare comunque un certocheckout usate la flag force

$ git checkout -f HEAD^

38

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 39: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Drsquoaltro canto se specificate un percorso particolare per il checkout non ci sonocontrolli di sicurezza I percorsi forniti sono silenziosamente sovrascritti Siatecauti se utilizzate checkout in questa modalitagrave

Reset Anche reset non funziona in presenza di cambiamenti non integrate concommit Per forzare il comando eseguite

$ git reset --hard 1b6d

Branch Non si possono cancellare branch se questo risulta nella perdita dicambiamenti Per forzare lrsquoeliminazione scrivete

$ git branch -D branch_da_cancellare invece di -d

Similmente un tentativo di rinominare una branch con il nome di unrsquoaltra egravebloccato se questo risulterebbe nella perdita di dati Per forzare il cambiamentodi nome scrivete

$ git branch -M origine destinazione agrave invece di -m

Contrariamente ai casi di checkout e reset questi ultimi comandi non effettuanounrsquoeliminazione immediata dellrsquoinformazione I cambiamenti sono salvati nellasottocartella git e possono essere recuperati tramite il corrispondente codicehash in gitlogs (vedete rdquoCacciatore di ldquotesterdquordquo precedentemente) Per de-fault sono conservati per almeno due settimane

Clean Alcuni comandi Git si rifiutano di procedere per non rischiare di dan-neggiare file che non sono in gestione Se siete certi che tutti questi file possonoessere sacrificati allora cancellateli senza pietagrave con

$ git clean -f -d

In seguito il comando precedentemente eccessivamente prudente funzioneragrave

Prevenire commit erronei

Errori stupidi ingombrano i miei depositi I peggiori sono quelli dovuti a filemancanti per via di un git add dimenticato Altri errori meno gravi riguardanospazi bianchi dimenticati e conflitti di merge irrisolti nonostante siano inoffen-sivi vorrei che non apparissero nel registro pubblico

Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati icosiddetti hook che mi avvisino di questi problemi comuni

$ cd githooks$ cp pre-commitsample pre-commit Vecchie versioni di Git chmod +x pre-commit

Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti dimerge non risolti

Per questa guida ho anche aggiunto le seguenti linee allrsquoinizio del mio hookpre-commit per prevenire le mie distrazioni

39

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 40: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

if git ls-files -o | grep txt$ thenecho FAIL Untracked txt filesexit 1

fi

Molte operazioni di Git accettano hook vedete git help hooks Abbiamogiagrave utilizzato lrsquohook post-update in precedenza quando abbiamo discusso Gitvia HTTP Questo egrave eseguito ogni volta che lrsquoHEAD cambia Lo script post-update drsquoesempio aggiorna i file Git necessari per comunicare dati via canalicome HTTP che sono agnostici di Git

Segreti rivelati

Diamo ora unrsquoocchiata sotto il cofano e cerchiamo di capire come Git realizzai suoi miracoli Per una descrizione approfondita fate riferimento al manualeutente

Invisibilitagrave

Come fa Git ad essere cosigrave discreto A parte qualche commit o merge occa-sionale potreste lavorare come se il controllo di versione non esistesse Vale adire fino a che non egrave necessario nel qual caso sarete felici che Git stava tenendotutto sotto controllo per tutto il tempo

Altri sistemi di controllo di versione vi forzano costantemente a confrontarvicon scartoffie e burocrazia File possono essere solo acceduti in lettura ameno che non dite esplicitamente al server centrale quali file intendete modi-ficare I comandi di base soffrono progressivamente di problemi di performanceallrsquoaumentare del numero utenti Il lavoro si arresta quando la rete o il servercentrale hanno problemi

In contrasto Git conserva tutta la storia del vostro progetto nella sottocartellagit della vostra cartella di lavoro Questa egrave la vostra copia personale dellastoria e potete quindi rimanere offline fino a che non volete comunicare con altriAvete controllo totale sul fato dei vostri file percheacute Git puograve ricrearli ad ognimomento a partire da uno stato salvato in git

Integritagrave

La maggior parte della gente associa la crittografia con la conservazione di infor-mazioni segrete ma un altro dei suoi importanti scopi egrave di conservare lrsquointegritagravedi queste informazioni Un uso appropriato di funzioni hash crittografiche puograveprevenire la corruzione accidentale e dolosa di dati

40

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 41: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Un codice hash SHA1 puograve essere visto come un codice unico di identificazionedi 160 bit per ogni stringa di byte concepibile

Visto che un codice SHA1 egrave lui stesso una stringa di byte possiamo calcolareun codice hash di stringe di byte che contengono altri codici hash Questasemplice osservazione egrave sorprendentemente utile cercate ad esempio hash chainsPiugrave tardi vedremo come Git usa questa tecnica per garantire efficientementelrsquointegritagrave di dati

Brevemente Git conserva i vostri dati nella sottocartella gitobjects mainvece di normali nomi di file vi troverete solo dei codici Utilizzando questicodici come nomi dei file e grazie a qualche trucco basato sullrsquouso di lockfile etimestamping Git trasforma un semplice sistema di file in un database efficientee robusto

Intelligenza

Come fa Git a sapere che avete rinominato un file anche se non glielrsquoavetemai detto esplicitamente Egrave vero magari avete usato git mv ma questo egraveesattamente la stessa cosa che usare git rm seguito da git add

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versionisuccessive Infatti puograve addirittura identificare lo spostamento di parti di codiceda un file ad un altro Pur non potendo coprire tutti i casi questo funziona moltobene e sta sempre costantemente migliorando Se non dovesse funzionare per voiprovate le opzioni che attivano metodi di rilevamento di copie piugrave impegnativee considerate lrsquoeventualitagrave di fare un aggiornamento

Indicizzazione

Per ogni file in gestione Git memorizza delle informazioni come la sua tagliasu disco e le date di creazione e ultima modifica un file detto indice Per deter-minare su un file egrave stato cambiato Git paragona il suo stato corrente con quelloche egrave memorizzato nellrsquoindice Se le due fonti di informazione corrispondono Gitnon ha bisogno di rileggere il file

Visto che lrsquoaccesso allrsquoindice egrave considerabilmente piugrave che leggere file se modifi-cate solo qualche file Git puograve aggiornare il suo stato quasi immediatamente

Prima abbiamo detto che lrsquoindice si trova nellrsquoarea di staging Comrsquoegrave possibileche un semplice file contenente dati su altri file si trova nellrsquoarea di stagingPercheacute il comando add aggiunge file nel database di Git e aggiorna queste in-formazioni mentre il comando commit senza opzioni crea un commit basatounicamente sullrsquoindice e i file giagrave inclusi nel database

41

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 42: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Le origini di Git

Questo messaggio della mailing list del kernel di Linux descrive la catena dieventi che hanno portato alla creazione di Git Lrsquointera discussione egrave un affasci-nante sito archeologico per gli storici di Git

Il database di oggetti

Ognuna delle versioni dei vostri dati egrave conservata nel cosiddetto database dioggetti che si trova nella sottocartella gitobjects il resto del contenuto digit rappresenta meno dati lrsquoindice il nome delle branch le tags le opzionidi configurazione i logs la posizione attuale del commit HEAD e cosigrave via Ildatabase di oggetti egrave semplice ma elegante e egrave la fonte della potenza di Git

Ogni file in gitobjects egrave un oggetto Ci sono tre tipi di oggetti che ciriguardano oggetti blob oggetti albero (o tree) e gli oggetti commit

Oggetti blob

Prima di tutto un porsquo di magia Scegliete un nome di file qualsiasi In unacartella vuota eseguite

$ echo sweet gt VOSTRO_FILE$ git init$ git add $ find gitobjects -type f

Vedrete gitobjectsaa823728ea7d592acc69b36875a482cdf3fd5c8d

Come posso saperlo senza sapere il nome del file Percheacute il codice hash SHA1di

blob SP 6 NUL sweet LF

egrave aa823728ea7d592acc69b36875a482cdf3fd5c8d dove SP egrave uno spazio NUL egraveun carattere di zero byte e LF un passaggio a nuova linea Potete verificaretutto ciograve digitando

$ printf blob 6000sweetn | sha1sum

Git utilizza un sistema di classificazione per contenuti i file non sono archiviatisecondo il loro nome ma secondo il codice hash del loro contenuto in un file chechiamiamo un oggetto blob Possiamo vedere il codice hash come identificativounico del contenuto del file Quindi in un certo senso ci stiamo riferendo ai filerispetto al loro contenuto Lrsquoiniziale blob 6 egrave semplicemente unrsquointestazioneche indica il tipo di oggetto e la sua lunghezza in bytes serve a semplificare lagestione interna

42

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 43: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Ecco come ho potuto predire il contenuto di git Il nome del file non contasolo il suo contenuto egrave usato per costruire lrsquooggetto blob

Magari vi state chiedendo che cosa succede nel caso di file identici Provate ad ag-giungere copie del vostro file con qualsiasi nome Il contenuto di gitobjectsrimane lo stesso a prescindere del numero di copie aggiunte Git salva i datisolo una volta

A proposito i file in gitobjects sono copressi con zlib e conseguentementenon potete visualizzarne direttamente il contenuto Passatele attraverso il filtrozpipe -d o eseguite

$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente lrsquooggetto scelto

Oggetti tree

Ma dove vanno a finire i nomi dei file Devono essere salvati da qualche parteGit si occupa dei nomi dei file in fase di commit

$ git commit Scrivete un messaggio$ find gitobjects -type f

Adesso dovreste avere tre oggetti Ora non sono piugrave in grado di predire ilnome dei due nuovi file percheacute dipenderagrave in parte dal nome che avete sceltoProcederemo assumendo che avete scelto ldquoroserdquo Se questo non fosse il casopotete sempre riscrivere la storia per far sembrare che lo sia

$ git filter-branch --tree-filter mv NOME_DEL_VOSTRO_FILE rose$ find gitobjects -type f

Adesso dovreste vedere il file gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9percheacute questo egrave il codice hash SHA1 del contenuto seguente

tree SP 32 NUL 100644 rose NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d

Verificate che questo file contenga il contenuto precedente digitando

$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch

Egrave piugrave facile verificare il codice hash con zpipe

$ zpipe -d lt gitobjects05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare lrsquohash egrave piugrave complicato con il comando cat-file percheacute il suo outputcontiene elementi ulteriori oltre al file decompresso

Questo file egrave un oggetto tree una lista di elementi consistenti in un tipo di fileun nome di file e un hash Nel nostro esempio il tipo di file egrave 100644 che indicache rose egrave un file normale e il codice hash e il codice hash egrave quello di un oggettodi tipo blob che contiene il contenuto di rose Altri possibili tipi di file sono

43

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 44: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

eseguibili link simbolici e cartelle Nellrsquoultimo caso il codice hash si riferisce adun oggetto tree

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete piugravebisogno Anche se saranno cancellati automaticamente dopo il periodo di riten-zione automatica ora li cancelleremo per rendere il nostro esempio piugrave facile daseguire

$ rm -r gitrefsoriginal$ git reflog expire --expire=now --all$ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del generevisto che distruggono dei backup Se volete un deposito piugrave ordinato egrave normal-mente consigliabile creare un nuovo clone Fate inoltre attenzione a manipolaredirettamente il contenuto di git che cosa succederebbe se un comando Git egravein esecuzione allo stesso tempo o se se ci fosse un improvviso calo di correnteIn generale i refs dovrebbero essere cancellati con git update-ref -d anche sespesso sembrerebbe sicuro cancella re refsoriginal a mano

Oggetti commit

Abbiamo spiegato 2 dei 3 tipi di oggetto Il terzo egrave lrsquooggetto commit Il suocontenuto dipende dal messaggio di commit come anche dalla data e lrsquoora incui egrave stato creato Percheacute far in maniera di ottenere la stessa cosa dobbiamofare qualche ritocco

$ git commit --amend -m Shakespeare Cambiamento del messaggio di commit$ git filter-branch --env-filter export

GIT_AUTHOR_DATE=Fri 13 Feb 2009 153130 -0800GIT_AUTHOR_NAME=AliceGIT_AUTHOR_EMAIL=aliceexamplecomGIT_COMMITTER_DATE=Fri 13 Feb 2009 153130 -0800GIT_COMMITTER_NAME=Bob

GIT_COMMITTER_EMAIL=bobexamplecom Ritocco della data di creazione e degli autori$ find gitobjects -type f

Dovreste ora vedere il file gitobjects49993fe130c4b3bf24857a15d7969c396b7bc187che egrave il codice hash SHA1 del suo contenuto

commit 158 NULtree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 LFauthor Alice ltaliceexamplecomgt 1234567890 -0800 LFcommitter Bob ltbobexamplecomgt 1234567890 -0800 LFLFShakespeare LF

Come prima potete utilizzare zpipe o cat-file per verificare voi stessi

44

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 45: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Questo egrave il primo commi non ci sono quindi commit genitori Ma i commitseguenti conterranno sempre almeno una linea che identifica un commit genitore

Indistinguibile dalla magia

I segreti di Git sembrano troppo semplici Sembra che basterebbe mescolareassieme qualche script shell e aggiungere un pizzico di codice C per preparare unsistema del genere in qualche ora una combinazione di operazioni di filesystemdi base e hashing SHA1 guarnito con lockfile e file di sincronizzazione peravere un porsquo di robustezza Infatti questaegrave un descrizione accurata le primeversioni di Git Malgrado ciograve a parte qualche astuta tecnica di compressioneper risparmiare spazio e di indicizzazione per risparmiare tempo ora sappiamocome Git cambia abilmente un sistema di file in un perfetto database per ilcontrollo di versione

Ad esempio se un file nel database degli oggetti egrave corrotto da un errore suldisco i codici hash non corrisponderanno piugrave e verremo informati del problemaCalcolando il codice hash del codice hash di altri oggetti egrave possibile garantireintegritagrave a tutti i livelli I commit sono atomici nel senso che un commit nonpuograve memorizzare modifiche parziali possiamo calcolare il codice hash di uncommit e salvarlo in un database dopo aver creato i relativi oggetti tree blob ecommit Il database degli oggetti egrave immune da interruzioni inaspettate dovutead esempio a cali di corrente

Possiamo anche far fronte ai tentativi di attacco piugrave maliziosi Supponiamo adesempio che un avversario tenti di modificare di nascosto il contenuto di un filein una vecchia versione di un progetto Per rendere il database degli oggetti coer-ente il nostro avversario deve anche modificare il codice hash dei corrispondentioggetti blob visto che ora saragrave una stringa di byte diversa Questo significa chedovragrave cambiare il codice hash di tutti gli oggetti tree che fanno riferimento alfile e di conseguenza cambiare lrsquohash di tutti gli oggetti commit in ognuno diquesti tree oltre ai codici hash di tutti i discendenti di questi commit Questoimplica che il codice hash dellrsquoHEAD ufficiale differiragrave da quello del depositocorrotto Seguendo la traccia di codici hash erronei possiamo localizzare conprecisione il file corrotto come anche il primo commit ad averlo introdotto

In conclusione purcheacute i 20 byte che rappresentano lrsquoultimo commit sono alsicuro egrave impossibile manomettere il deposito Git

Che dire delle famose funzionalitagrave di Git Della creazione di branch Deimerge Delle tag Semplici dettagli LrsquoHEAD corrente egrave conservata nel filegitHEAD che contiene un codice hash di un oggetto commit Il codice hashviene aggiornato durante un commit e lrsquoesecuzione di molti altri comandi Lebranch funzionano in maniera molto simile sono file in gitrefsheads Lastessa cosa vale per le tag salvate in gitrefstags ma sono aggiornate daun insieme diverso di comandi

45

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 46: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Le lacune di Git

Git presenta qualche problema che ho nascosto sotto il tappeto Alcuni possonoessere facilmente risolti con script e hook altri richiedono di riorganizzare eridefinire il progetto e per le poche rimanenti seccature non vi rimarragrave cheattendere O meglio ancora contribuire con il vostro aiuto

Le debolezze di SHA1

Con il tempo gli specialisti in crittografia continuano a scoprire debolezze diSHA1 Egrave giagrave possibile trovare collisioni hash (cioegrave sequenze di byte che risultanonello stesso codice hash) dati sufficienti mezzi Fra qualche anno anche unnormale PC potrebbe avere abbastanza potenza di calcolo per corrompere inmaniera non rilevabile un deposito Git

Auspicabilmente Git saragrave migrato verso un migliore sistema di funzioni hashprima che ulteriore ricerca distruggeragrave lo standard SHA1

Microsoft Windows

Git per Microsoft Windows puograve essere piuttosto ingombrante

bull Cygwin egrave un ambiente di emulazione di Linux per Windows che contieneuna versione di Git per Windows

bull Git per Windows egrave un alternativa che richiede meno risorse anche sealcuni comandi necessitano ancora di essere migliorati

File senza relazione

Se il vostro progetto egrave molto grande e contiene molti file scorrelati che tendonoa cambiare spesso Git puograve essere in svantaggio rispetto ad altri sistemi percheacutefile singoli non sono tenuti sotto controllo Git tiene sotto controllo lrsquointeroprogetto che normalmente egrave una strategia vantaggiosa

Una soluzione egrave di suddividere il vostro progetto in pezzi ognuno consistente digruppi di file correlati Usate git submodule se volete poi comunque manteneretutto in un deposito unico

Chi modifica cosa

Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente unfile prima di poterlo modificare Mentre questo egrave particolarmente fastidioso

46

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 47: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

percheacute implica comunicazioni addizionali con un server centrale ha comunquedue benefici

1 Il calcolo delle differenze egrave rapido percheacute solo i file marcati devono essereesaminati

2 Ognuno puograve sapere chi sta lavorando su un file chiedendo al server centralechi lrsquoha marcato per modifiche

Con qualche script appropriato potete ottenere la stessa cosa con Git Questorichiede cooperazione dagli altri programmatori i quali devono eseguire scriptparticolari prima di modificare un file

La storia di un file

Percheacute Git registra modifiche in maniera globale al progetto la ricostruzionedella storia di un singolo file richiede piugrave lavoro che in altri sistemi di controllodi versioni che si occupano di file individuali

Questo sovrappiugrave egrave generalmente trascurabile e ne vale la pena visto che perme-tte altre operazioni di incredibile efficienza Per esempio git checkout egrave piugraverapido che cp -a e una differenza di versione globale al progetto si comprimemeglio che una collezione di differenze di file individuali

Il clone iniziale

Creare un clone egrave piugrave costoso che fare un checkout in altri sistemi di controllodi versione se il progetto ha una storia lunga

Il costo iniziale egrave un buon investimento visto che operazioni future sarannopiugrave rapide e offline Tuttavia in alcune situazioni puograve essere preferibile creareun clone superficiale utilizzando lrsquoopzione --depth Questo egrave piugrave rapido ma ilclone risultante ha funzionalitagrave limitate

Progetti volatili

Git egrave stato scritto per essere rapido rispetto alla dimensione dei cambiamentiNormalmente si tende a fare piccole modifiche da una versione allrsquoaltra La cor-rezione di un bug in una linea qui una nuova funzionalitagrave lagrave commenti correttie cosigrave via Ma se i vostri file cambiano radicalmente in revisioni successive adogni commit la vostra storia cresceragrave necessariamente proporzionalmente alledimensioni dellrsquointero progetto

Non crsquoegrave niente che nessun sistema di controllo di versione possa fare per evitarequesto ma gli utilizzatori di Git ne soffriranno di piugrave percheacute ogni clone contienenormalmente la storia completa

47

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 48: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Bisogna cercare la ragione per cui questi cambiamenti sono cosigrave grandi Magaribisogna cambiare il formato dei file Modifiche minori dovrebbero causare solocambiamenti minori in solo pochi file

Magari un database o un sistema drsquoarchivio sono invece una soluzione piugrave adattainvece di un sistema di controllo di versione Per esempio un sistema di con-trollo di versione potrebbe non essere adatto per gestire fotografie prese peri-odicamente da una webcam

Se i file devono essere cambiare radicalmente e se devono essere gestite in ver-sioni una possibilitagrave egrave di usare Git in maniera centralizzata Egrave possibile crearecloni superficiali che contengono solo una parte minore o addirittura inesistentedella storia del progetto Naturalmente in questo caso molti strumenti di Gitnon saranno piugrave a disposizione e correzioni dovranno essere fornite sotto formadi patch Questo va probabilmente bene visto che non sembrerebbe doverciessere nessun motivo per mantenere la storia di file ampiamente instabili

Un altro esempio egrave un progetto che dipende da un firmware che consiste in unenorme file binario La storia di questo firmware non interessa agli utilizzatorie gli aggiornamenti non sono molto compressibili il che significa che le revisionidel firmware inflazionano inutilmente il deposito

In questo caso il codice sorgente dovrebbe essere salvato in un deposito Gitmentre i file binari dovrebbero essere tenuti separatamente Per rendere la vitapiugrave facile si potrebbe distribuire uno script che usa Git per clonare il codicesorgente e rsync o un Git superficiale per il firmware

Contatore globale

Alcuni sistemi di controllo di versione centralizzati mantengono un numero in-tero che aumenta quando un nuovo commit egrave accettato Git fa riferimentoai cambiamenti tramite il loro codice hash un metodo migliore in molte cir-costanze

Alcune persone vorrebbero perograve avere accesso a questo contatore Fortunata-mente egrave facile scrivere uno script che fa in maniera di aumentare un contatore neldeposito Git centrale ad ogni aggiornamento magari grazie ad una tag associatacon un hash dellrsquoultimo commit

Ogni clone potrebbe mantenere un tale contatore ma questo sarebbe probabil-mente inutile visto che solo il contatore del deposito centrale egrave interessante pergli utenti

Sottocartelle vuote

Sottocartelle vuote non possono essere gestite Create dei file segnaposto perrimediare a questo problema

48

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 49: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Queste limitazioni non sono dovute a come Git egrave concepito ma piuttosto a comeegrave correntemente implementato Con un porsquo di fortuna se abbastanza utilizzatorilo richiedono questa funzionalitagrave potrebbe essere implementata

Commit iniziale

In informatico tipico conta a partire da 0 invece che da 1 Sfortunatamenterispetto ai commit Git non aderisce a questa convenzione Molti comandi nonfunzionano prima del primo commit Inoltre alcuni casi limite devono esseregestiti in maniera specifica ad esempio usare rebase su una branch con commitiniziale diverso

Git beneficerebbe della definizione del commit numero zero non appena undeposito egrave costruito HEAD verrebbe assegnato ad una stringa consistente in20 bytes zero Questo commit speciale rappresenterebbe un tree vuoto senzagenitori che sarebbe presente in tutti i depositi Git

In questo modo ad esempio lrsquoesecuzione di git log informerebbe lrsquoutente che nonsono ancora stati fatti commit invece di terminare con un fatal error Una cosasimile varrebbe per altri comandi

Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero

Ci sarebbero tuttavia casi problematici Se diverse branch con commit inizialidiversi fossero fusi assieme con un merge lrsquouso del comando rebase richiederebbeun sostanziale intervento manuale

Bizzarrie dellrsquointerfaccia

Dati due commit A e B il significato delle espressioni rdquoABrdquo e rdquoAhellipBrdquo dipendeda se il comando si attende due estremitagrave o un intervallo Vedete git help diffe git help rev-parse

Tradurre questo manuale

La mia raccomandazione egrave di rispettare la seguente procedura per la traduzionedi questo manuale in maniera da poter rapidamente generare le versioni HTMLe PDF del documento con gli script forniti e cosigrave che tutte le traduzioni sianoincorporate nello stesso deposito

Fate un clone della sorgente poi create una directory il cui nome corrispondaal codice IETF della lingua desiderata vedere lrsquoarticolo del W3C concernenteinternazionalizzazione Per esempio la versione in inglese egrave nella directory rdquoenrdquoquella in giapponese egrave in rdquojardquo Nella nuova directory traducete i file txt delladirectory originari rdquoenrdquo

49

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale
Page 50: Git Magic - Stanford CS Theorytheory.stanford.edu/~blynn/gitmagic/intl/it/book.pdf · Git Magic Ben Lynn Agosto 2007 ... Introduzione Farò uso di un’analogia per introdurre il

Per esempio per creare questa guida in Klingon eseguite

$ git clone gitrepoorczgitmagicgit$ cd gitmagic$ mkdir tlh tlh egrave il codice IETF della lingua Klingon$ cd tlh$ cp enintrotxt $ edit introtxt Tradurre il file

e cosigrave di seguito per tutti i file

Modificate il Makefile aggiungendo il codice della lingua alla variabileTRANSLATIONS In questo modo potete rivedere il vostro lavoro in modoincrementale

$ make tlh$ firefox book-tlhindexhtml

Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sonostate implementate Github possiede unrsquointerfaccia che facilita il lavoro collabo-rativo fate un fork del progetto rdquogitmagicrdquo fate un push delle vostre modifichee chiedetemi di incorporarle

50

  • Prefazione
    • Grazie
    • Licenza
      • Introduzione
        • Il lavoro egrave gioco
        • Controllo di versione
        • Controllo distribuito
        • Una sciocca superstizione
        • Merge di conflitti
          • Trucchi di base
            • Salvare lo stato corrente
            • Aggiungere rimuovere e rinominare file
            • AnnullareRipristino avanzati
            • Annullare (revert)
            • Generare un diario delle modifiche (changelog)
            • Scaricare dei files
            • Lultima versione
            • Pubblicazione istantanea
            • Che cosa ho fatto
            • Esercizio
              • Cloniamo
                • Sincronizzazione tra computers
                • Controllo classico di file sorgente
                • File sorgente segreti
                • Depositi nudi
                • Push vs pull
                • Fare il forking di un progetto
                • Il sistema definitivo di salvataggio
                • Multi-tasking alla velocitagrave della luce
                • Controllo di versione da battaglia
                • Mercurial
                • Bazaar
                • Percheacute utilizzo Git
                  • La stregoneria delle branch
                    • Boss key
                    • Lavoro temporaneo
                    • Correzioni rapide
                    • Merge
                    • Flusso di lavoro ininterrotto
                    • Riorganizzare un pasticcio
                    • Gestione di branch
                    • Branch temporanee
                    • Lavorate come volete
                      • Lezioni di storia
                        • Mi correggo
                        • hellip e ancora di piugrave
                        • E cambiamenti locali per finire
                        • Riscrivere la storia
                        • Fare la storia
                        • Dovegrave che ho sbagliato
                        • Chi egrave che ha sbagliato
                        • Esperienza personale
                          • Git multi-giocatore
                            • Chi sono
                            • Git via SSH e HTTP
                            • Git tramite qualsiasi canale
                            • Le patch la moneta di scambio globale
                            • Ci dispiace abbiamo cambiato indirizzo
                            • Branch remote
                            • Depositi remoti multipli
                            • Le mie preferenze
                              • Padroneggiare Git
                                • Pubblicazione di codice sorgente
                                • Commit dei cambiamenti
                                • Il mio commit egrave troppo grande
                                • Lindice larea di staging
                                • Non perdete la testa
                                • Cacciatore di teste
                                • Costruire sopra Git
                                • Acrobazie audaci
                                • Prevenire commit erronei
                                  • Segreti rivelati
                                    • Invisibilitagrave
                                    • Integritagrave
                                    • Intelligenza
                                    • Indicizzazione
                                    • Le origini di Git
                                    • Il database di oggetti
                                    • Oggetti blob
                                    • Oggetti tree
                                    • Oggetti commit
                                    • Indistinguibile dalla magia
                                      • Le lacune di Git
                                        • Le debolezze di SHA1
                                        • Microsoft Windows
                                        • File senza relazione
                                        • Chi modifica cosa
                                        • La storia di un file
                                        • Il clone iniziale
                                        • Progetti volatili
                                        • Contatore globale
                                        • Sottocartelle vuote
                                        • Commit iniziale
                                        • Bizzarrie dellinterfaccia
                                          • Tradurre questo manuale