Merge branch 'master' of https://github.com/matrig/gitmagic
[gitmagic.git] / it / secrets.txt
blob51622db91398c04f0dc04e5d4b1b5f8b24b57e86
1 == Segreti rivelati ==
3 Diamo ora un'occhiata sotto il cofano e cerchiamo di capire come Git
4 realizza i suoi miracoli. Per una descrizione approfondita fate
5 riferimento al
6 http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[manuale
7 utente].
9 === Invisibilità ===
11 Come fa Git ad essere così discreto? A parte qualche commit o merge
12 occasionale, potreste lavorare come se il controllo di versione non
13 esistesse. Vale a dire fino a che non è necessario, nel qual caso sarete
14 felici che Git stava tenendo tutto sotto controllo per tutto il tempo.
16 Altri sistemi di controllo di versione vi forzano costantemente a
17 confrontarvi con scartoffie e burocrazia. File possono essere solo
18 acceduti in lettura, a meno che non dite esplicitamente al server
19 centrale quali file intendete modificare. I comandi di base soffrono
20 progressivamente di problemi di performance all'aumentare del numero
21 utenti. Il lavoro si arresta quando la rete o il server centrale hanno
22 problemi.
24 In contrasto, Git conserva tutta la storia del vostro progetto nella
25 sottocartella `.git` della vostra cartella di lavoro. Questa è la vostra
26 copia personale della storia e potete quindi rimanere offline fino a che
27 non volete comunicare con altri. Avete controllo totale sul fato dei
28 vostri file perché Git può ricrearli ad ogni momento a partire da uno
29 stato salvato in `.git`.
31 === Integrità  ===
33 La maggior parte della gente associa la crittografia con la
34 conservazione di informazioni segrete ma un altro dei suoi importanti
35 scopi è di conservare l'integrità di queste informazioni. Un uso
36 appropriato di funzioni hash crittografiche può prevenire la corruzione
37 accidentale e dolosa di dati.
39 Un codice hash SHA1 può essere visto come un codice unico di
40 identificazione di 160 bit per ogni stringa di byte concepibile.
42 Visto che un codice SHA1 è lui stesso una stringa di byte, possiamo
43 calcolare un codice hash di stringe di byte che contengono altri codici
44 hash. Questa semplice osservazione è sorprendentemente utile: cercate ad
45 esempio 'hash chains'. Più tardi vedremo come Git usa questa tecnica per
46 garantire efficientemente l'integrità di dati.
48 Brevemente, Git conserva i vostri dati nella sottocartella
49 `.git/objects`, ma invece di normali nomi di file vi troverete solo dei
50 codici. Utilizzando questi codici come nomi dei file, e grazie a qualche
51 trucco basato sull'uso di 'lockfile' e 'timestamping', Git trasforma un
52 semplice sistema di file in un database efficiente e robusto.
54 === Intelligenza ===
56 Come fa Git a sapere che avete rinominato un file anche se non
57 gliel'avete mai detto esplicitamente? È vero, magari avete usato *git
58 mv*, ma questo è esattamente la stessa cosa che usare *git rm* seguito
59 da *git add*.
61 Git possiede dei metodi euristici stanare cambiamenti di nomi e copie
62 tra versioni successive. Infatti, può addirittura identificare lo
63 spostamento di parti di codice da un file ad un altro! Pur non potendo
64 coprire tutti i casi, questo funziona molto bene e sta sempre
65 costantemente migliorando. Se non dovesse funzionare per voi, provate
66 le opzioni che attivano metodi di rilevamento di copie più impegnative,
67 e considerate l'eventualità di fare un aggiornamento
69 === Indicizzazione  ===
71 Per ogni file in gestione, Git memorizza delle informazioni, come la sua
72 taglia su disco, e le date di creazione e ultima modifica, un file detto
73 'indice'. Per determinare su un file è stato cambiato, Git paragona il
74 suo stato corrente con quello che è memorizzato nell'indice. Se le due
75 fonti di informazione corrispondono Git non ha bisogno di rileggere il
76 file.
78 Visto che l'accesso all'indice è considerabilmente più che leggere file,
79 se modificate solo qualche file, Git può aggiornare il suo stato quasi
80 immediatamente.
82 Prima abbiamo detto che l'indice si trova nell'area di staging. Com'è
83 possibile che un semplice file contenente dati su altri file si trova
84 nell'area di staging? Perché il comando 'add' aggiunge file nel database
85 di Git e aggiorna queste informazioni, mentre il comando 'commit' senza
86 opzioni crea un commit basato unicamente sull'indice e i file già
87 inclusi nel database.
89 === Le origini di Git ===
91 Questo http://lkml.org/lkml/2005/4/6/121[messaggio della mailing list
92 del kernel di Linux] descrive la catena di eventi che hanno portato alla
93 creazione di Git. L'intera discussione è un affascinante sito
94 archeologico per gli storici di Git.
96 === Il database di oggetti ===
98 Ognuna delle versioni dei vostri dati è conservata nel cosiddetto
99 'database di oggetti' che si trova nella sottocartella `.git/objects`;
100 il resto del contenuto di `.git/` rappresenta meno dati: l'indice, il
101 nome delle branch, le tags, le opzioni di configurazione, i logs, la
102 posizione attuale del commit HEAD, e così via. Il database di oggetti è
103 semplice ma elegante, e è la fonte della potenza di Git.
105 Ogni file in `.git/objects` è un 'oggetto'. Ci sono tre tipi di oggetti
106 che ci riguardano: oggetti 'blob', oggetti 'albero' (o `tree`) e gli
107 oggetti 'commit'.
109 === Oggetti 'blob' ===
111 Prima di tutto un po' di magia. Scegliete un nome di file qualsiasi. In
112 una cartella vuota eseguite:
114  $ echo sweet > VOSTRO_FILE
115  $ git init
116  $ git add .
117  $ find .git/objects -type f
119 Vedrete +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+.
121 Come posso saperlo senza sapere il nome del file? Perché il codice hash
122 SHA1 di:
124  "blob" SP "6" NUL "sweet" LF
126 è aa823728ea7d592acc69b36875a482cdf3fd5c8d, dove SP è uno spazio, NUL è
127 un carattere di zero byte e LF un passaggio a nuova linea. Potete
128 verificare tutto ciò digitando:
130   $ printf "blob 6\000sweet\n" | sha1sum
132 Git utilizza un sistema di classificazione per contenuti: i file non
133 sono archiviati secondo il loro nome, ma secondo il codice hash del loro
134 contenuto, in un file che chiamiamo un oggetto 'blob'. Possiamo vedere
135 il codice hash come identificativo unico del contenuto del file. Quindi,
136 in un certo senso, ci stiamo riferendo ai file rispetto al loro
137 contenuto. L'iniziale `blob 6` è semplicemente un'intestazione che
138 indica il tipo di oggetto e la sua lunghezza in bytes; serve a
139 semplificare la gestione interna.
141 Ecco come ho potuto predire il contenuto di .git. Il nome del file non
142 conta: solo il suo contenuto è usato per costruire l'oggetto blob.
144 Magari vi state chiedendo che cosa succede nel caso di file identici.
145 Provate ad aggiungere copie del vostro file, con qualsiasi nome. Il
146 contenuto di +.git/objects+ rimane lo stesso a prescindere del numero di
147 copie aggiunte. Git salva i dati solo una volta.
149 A proposito, i file in +.git/objects+ sono copressi con zlib e
150 conseguentemente non potete visualizzarne direttamente il contenuto.
151 Passatele attraverso il filtro http://www.zlib.net/zpipe.c[zpipe -d], o
152 eseguite:
154  $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d
156 che visualizza appropriatamente l'oggetto scelto.
158 === Oggetti 'tree' ===
160 Ma dove vanno a finire i nomi dei file? Devono essere salvati da qualche
161 parte. Git si occupa dei nomi dei file in fase di commit:
163  $ git commit  # Scrivete un messaggio
164  $ find .git/objects -type f
166 Adesso dovreste avere tre oggetti. Ora non sono più in grado di predire
167 il nome dei due nuovi file, perché dipenderà in parte dal nome che avete
168 scelto. Procederemo assumendo che avete scelto ``rose''. Se questo non
169 fosse il caso potete sempre riscrivere la storia per far sembrare che lo
170 sia:
172  $ git filter-branch --tree-filter 'mv NOME_DEL_VOSTRO_FILE rose'
173  $ find .git/objects -type f
175 Adesso dovreste vedere il file
176 +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+
177 perché questo è il codice hash SHA1 del contenuto seguente:
179  "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d
181 Verificate che questo file contenga il contenuto precedente digitando:
183  $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch
185 È più facile verificare il codice hash con zpipe:
187  $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum
189 Verificare l'hash è più complicato con il comando cat-file perché il suo
190 output contiene elementi ulteriori oltre al file decompresso.
192 Questo file è un oggetto 'tree': una lista di elementi consistenti in un
193 tipo di file, un nome di file, e un hash. Nel nostro esempio il tipo di
194 file è 100644, che indica che `rose` è un file normale e il codice hash
195 e il codice hash è quello di un oggetto di tipo 'blob' che contiene il
196 contenuto di `rose`.  Altri possibili tipi di file sono eseguibili, link
197 simbolici e cartelle. Nell'ultimo caso il codice hash si riferisce ad un
198 oggetto 'tree'.
200 Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non
201 avete più bisogno. Anche se saranno cancellati automaticamente dopo il
202 periodo di ritenzione automatica, ora li cancelleremo per rendere il
203 nostro esempio più facile da seguire
205  $ rm -r .git/refs/original
206  $ git reflog expire --expire=now --all
207  $ git prune
209 Nel caso di un vero progetto dovreste tipicamente evitare comandi del
210 genere, visto che distruggono dei backup. Se volete un deposito più
211 ordinato, è normalmente consigliabile creare un nuovo clone. Fate
212 inoltre attenzione a manipolare direttamente il contenuto di +.git+: che
213 cosa succederebbe se un comando Git è in esecuzione allo stesso tempo, o
214 se se ci fosse un improvviso calo di corrente? In generale i refs
215 dovrebbero essere cancellati con *git update-ref -d*, anche se spesso
216 sembrerebbe sicuro cancella re +refs/original+ a mano.
218 === Oggetti 'commit' ===
220 Abbiamo spiegato 2 dei 3 tipi di oggetto. Il terzo è l'oggetto
221 'commit'. Il suo contenuto dipende dal messaggio di commit, come anche
222 dalla data e l'ora in cui è stato creato. Perché far in maniera di
223 ottenere la stessa cosa dobbiamo fare qualche ritocco:
225  $ git commit --amend -m Shakespeare  # Cambiamento del messaggio di commit
226  $ git filter-branch --env-filter 'export
227      GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800"
228      GIT_AUTHOR_NAME="Alice"
229      GIT_AUTHOR_EMAIL="alice@example.com"
230      GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
231      GIT_COMMITTER_NAME="Bob"
232      GIT_COMMITTER_EMAIL="bob@example.com"'  # Ritocco della data di creazione e degli autori
233  $ find .git/objects -type f
235 Dovreste ora vedere il file +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+
236 che è il codice hash SHA1 del suo contenuto:
238  "commit 158" NUL
239  "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF
240  "author Alice <alice@example.com> 1234567890 -0800" LF
241  "committer Bob <bob@example.com> 1234567890 -0800" LF
242  LF
243  "Shakespeare" LF
245 Come prima potete utilizzare zpipe o cat-file per verificare voi stessi.
247 Questo è il primo commi, non ci sono quindi commit genitori. Ma i commit
248 seguenti conterranno sempre almeno una linea che identifica un commit
249 genitore.
251 === Indistinguibile dalla magia ===
253 I segreti di Git sembrano troppo semplici. Sembra che basterebbe
254 mescolare assieme qualche script shell e aggiungere un pizzico di codice
255 C per preparare un sistema del genere in qualche ora: una combinazione
256 di operazioni di filesystem di base e hashing SHA1, guarnito con
257 lockfile e file di sincronizzazione per avere un po' di robustezza.
258 Infatti questaè un descrizione accurata le prime versioni di Git.
259 Malgrado ciò, a parte qualche astuta tecnica di compressione per
260 risparmiare spazio e di indicizzazione per risparmiare tempo, ora
261 sappiamo come Git cambia abilmente un sistema di file in un perfetto
262 database per il controllo di versione.
264 Ad esempio, se un file nel database degli oggetti è corrotto da un errore
265 sul disco i codici hash non corrisponderanno più e verremo informati del
266 problema. Calcolando il codice hash del codice hash di altri oggetti è
267 possibile garantire integrità a tutti i livelli. I commit sono atomici,
268 nel senso che un commit non può memorizzare modifiche parziali: possiamo
269 calcolare il codice hash di un commit e salvarlo in un database dopo
270 aver creato i relativi oggetti 'tree', 'blob' e 'commit'. Il database
271 degli oggetti è immune da interruzioni inaspettate dovute ad esempio a
272 cali di corrente.
274 Possiamo anche far fronte ai tentativi di attacco più maliziosi.
275 Supponiamo ad esempio che un avversario tenti di modificare di nascosto il
276 contenuto di un file in una vecchia versione di un progetto. Per rendere
277 il database degli oggetti coerente, il nostro avversario deve anche
278 modificare il codice hash dei corrispondenti oggetti blob, visto che ora
279 sarà una stringa di byte diversa. Questo significa che dovrà cambiare il
280 codice hash di tutti gli oggetti tree che fanno riferimento al file, e
281 di conseguenza cambiare l'hash di tutti gli oggetti commit in ognuno di
282 questi tree, oltre ai codici hash di tutti i discendenti di questi
283 commit. Questo implica che il codice hash dell'HEAD ufficiale differirà
284 da quello del deposito corrotto. Seguendo la traccia di codici hash
285 erronei possiamo localizzare con precisione il file corrotto, come anche
286 il primo commit ad averlo introdotto.
288 In conclusione, purché i 20 byte che rappresentano l'ultimo commit sono
289 al sicuro, è impossibile manomettere il deposito Git.
291 Che dire delle famose funzionalità di Git? Della creazione di branch?
292 Dei merge? Delle tag? Semplici dettagli. L'HEAD corrente è conservata
293 nel file +.git/HEAD+ che contiene un codice hash di un oggetto commit.
294 Il codice hash viene aggiornato durante un commit e l'esecuzione di
295 molti altri comandi. Le branch funzionano in maniera molto simile: sono
296 file in +.git/refs/heads+. La stessa cosa vale per le tag, salvate in
297 +.git/refs/tags+ ma sono aggiornate da un insieme diverso di comandi.