File: secrets.txt

package info (click to toggle)
gitmagic 20160304-1
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 2,280 kB
  • ctags: 20
  • sloc: makefile: 98; sh: 38
file content (297 lines) | stat: -rw-r--r-- 13,566 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
== Segreti rivelati ==

Diamo ora un'occhiata sotto il cofano e cerchiamo di capire come Git
realizza i suoi miracoli. Per una descrizione approfondita fate
riferimento al
http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[manuale
utente].

=== Invisibilità ===

Come fa Git ad essere così discreto? A parte qualche commit o merge
occasionale, potreste lavorare come se il controllo di versione non
esistesse. Vale a dire fino a che non è necessario, nel qual caso sarete
felici che Git stava tenendo tutto sotto controllo per tutto il tempo.

Altri sistemi di controllo di versione vi forzano costantemente a
confrontarvi con scartoffie e burocrazia. File possono essere solo
acceduti in lettura, a meno che non dite esplicitamente al server
centrale quali file intendete modificare. I comandi di base soffrono
progressivamente di problemi di performance all'aumentare del numero
utenti. Il lavoro si arresta quando la rete o il server centrale hanno
problemi.

In contrasto, Git conserva tutta la storia del vostro progetto nella
sottocartella `.git` della vostra cartella di lavoro. Questa è la vostra
copia personale della storia e potete quindi rimanere offline fino a che
non volete comunicare con altri. Avete controllo totale sul fato dei
vostri file perché Git può ricrearli ad ogni momento a partire da uno
stato salvato in `.git`.

=== Integrità  ===

La maggior parte della gente associa la crittografia con la
conservazione di informazioni segrete ma un altro dei suoi importanti
scopi è di conservare l'integrità di queste informazioni. Un uso
appropriato di funzioni hash crittografiche può prevenire la corruzione
accidentale e dolosa di dati.

Un codice hash SHA1 può essere visto come un codice unico di
identificazione di 160 bit per ogni stringa di byte concepibile.

Visto che un codice SHA1 è lui stesso una stringa di byte, possiamo
calcolare un codice hash di stringe di byte che contengono altri codici
hash. Questa semplice osservazione è sorprendentemente utile: cercate ad
esempio 'hash chains'. Più tardi vedremo come Git usa questa tecnica per
garantire efficientemente l'integrità di dati.

Brevemente, Git conserva i vostri dati nella sottocartella
`.git/objects`, ma invece di normali nomi di file vi troverete solo dei
codici. Utilizzando questi codici come nomi dei file, e grazie a qualche
trucco basato sull'uso di 'lockfile' e 'timestamping', Git trasforma un
semplice sistema di file in un database efficiente e robusto.

=== Intelligenza ===

Come fa Git a sapere che avete rinominato un file anche se non
gliel'avete mai detto esplicitamente? È vero, magari avete usato *git
mv*, ma questo è esattamente la stessa cosa che usare *git rm* seguito
da *git add*.

Git possiede dei metodi euristici stanare cambiamenti di nomi e copie
tra versioni successive. Infatti, può addirittura identificare lo
spostamento di parti di codice da un file ad un altro! Pur non potendo
coprire tutti i casi, questo funziona molto bene e sta sempre
costantemente migliorando. Se non dovesse funzionare per voi, provate
le opzioni che attivano metodi di rilevamento di copie più impegnative,
e considerate l'eventualità di fare un aggiornamento

=== Indicizzazione  ===

Per ogni file in gestione, Git memorizza delle informazioni, come la sua
taglia su disco, e le date di creazione e ultima modifica, un file detto
'indice'. Per determinare su un file è stato cambiato, Git paragona il
suo stato corrente con quello che è memorizzato nell'indice. Se le due
fonti di informazione corrispondono Git non ha bisogno di rileggere il
file.

Visto che l'accesso all'indice è considerabilmente più che leggere file,
se modificate solo qualche file, Git può aggiornare il suo stato quasi
immediatamente.

Prima abbiamo detto che l'indice si trova nell'area di staging. Com'è
possibile che un semplice file contenente dati su altri file si trova
nell'area di staging? Perché il comando 'add' aggiunge file nel database
di Git e aggiorna queste informazioni, mentre il comando 'commit' senza
opzioni crea un commit basato unicamente sull'indice e i file già
inclusi nel database.

=== Le origini di Git ===

Questo http://lkml.org/lkml/2005/4/6/121[messaggio della mailing list
del kernel di Linux] descrive la catena di eventi che hanno portato alla
creazione di Git. L'intera discussione è un affascinante sito
archeologico per gli storici di Git.

=== Il database di oggetti ===

Ognuna delle versioni dei vostri dati è conservata nel cosiddetto
'database di oggetti' che si trova nella sottocartella `.git/objects`;
il resto del contenuto di `.git/` rappresenta meno dati: l'indice, il
nome delle branch, le tags, le opzioni di configurazione, i logs, la
posizione attuale del commit HEAD, e così via. Il database di oggetti è
semplice ma elegante, e è la fonte della potenza di Git.

Ogni file in `.git/objects` è un 'oggetto'. Ci sono tre tipi di oggetti
che ci riguardano: oggetti 'blob', oggetti 'albero' (o `tree`) e gli
oggetti 'commit'.

=== Oggetti 'blob' ===

Prima di tutto un po' di magia. Scegliete un nome di file qualsiasi. In
una cartella vuota eseguite:

 $ echo sweet > VOSTRO_FILE
 $ git init
 $ git add .
 $ find .git/objects -type f

Vedrete +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+.

Come posso saperlo senza sapere il nome del file? Perché il codice hash
SHA1 di:

 "blob" SP "6" NUL "sweet" LF

è aa823728ea7d592acc69b36875a482cdf3fd5c8d, dove SP è uno spazio, NUL è
un carattere di zero byte e LF un passaggio a nuova linea. Potete
verificare tutto ciò digitando:

  $ printf "blob 6\000sweet\n" | sha1sum

Git utilizza un sistema di classificazione per contenuti: i file non
sono archiviati secondo il loro nome, ma secondo il codice hash del loro
contenuto, in un file che chiamiamo un oggetto 'blob'. Possiamo vedere
il codice hash come identificativo unico del contenuto del file. Quindi,
in un certo senso, ci stiamo riferendo ai file rispetto al loro
contenuto. L'iniziale `blob 6` è semplicemente un'intestazione che
indica il tipo di oggetto e la sua lunghezza in bytes; serve a
semplificare la gestione interna.

Ecco come ho potuto predire il contenuto di .git. Il nome del file non
conta: solo il suo contenuto è usato per costruire l'oggetto blob.

Magari vi state chiedendo che cosa succede nel caso di file identici.
Provate ad aggiungere copie del vostro file, con qualsiasi nome. Il
contenuto di +.git/objects+ rimane lo stesso a prescindere del numero di
copie aggiunte. Git salva i dati solo una volta.

A proposito, i file in +.git/objects+ sono copressi con zlib e
conseguentemente non potete visualizzarne direttamente il contenuto.
Passatele attraverso il filtro http://www.zlib.net/zpipe.c[zpipe -d], o
eseguite:

 $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d

che visualizza appropriatamente l'oggetto scelto.

=== Oggetti 'tree' ===

Ma dove vanno a finire i nomi dei file? Devono essere salvati da qualche
parte. Git si occupa dei nomi dei file in fase di commit:

 $ git commit  # Scrivete un messaggio
 $ find .git/objects -type f

Adesso dovreste avere tre oggetti. Ora non sono più in grado di predire
il nome dei due nuovi file, perché dipenderà in parte dal nome che avete
scelto. Procederemo assumendo che avete scelto ``rose''. Se questo non
fosse il caso potete sempre riscrivere la storia per far sembrare che lo
sia:

 $ git filter-branch --tree-filter 'mv NOME_DEL_VOSTRO_FILE rose'
 $ find .git/objects -type f

Adesso dovreste vedere il file
+.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+
perché questo è 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

È più facile verificare il codice hash con zpipe:

 $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum

Verificare l'hash è più complicato con il comando cat-file perché il suo
output contiene elementi ulteriori oltre al file decompresso.

Questo file è un oggetto 'tree': una lista di elementi consistenti in un
tipo di file, un nome di file, e un hash. Nel nostro esempio il tipo di
file è 100644, che indica che `rose` è un file normale e il codice hash
e il codice hash è quello di un oggetto di tipo 'blob' che contiene il
contenuto di `rose`.  Altri possibili tipi di file sono eseguibili, link
simbolici e cartelle. Nell'ultimo caso il codice hash si riferisce ad un
oggetto 'tree'.

Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non
avete più bisogno. Anche se saranno cancellati automaticamente dopo il
periodo di ritenzione automatica, ora li cancelleremo per rendere il
nostro esempio più facile da seguire

 $ rm -r .git/refs/original
 $ git reflog expire --expire=now --all
 $ git prune

Nel caso di un vero progetto dovreste tipicamente evitare comandi del
genere, visto che distruggono dei backup. Se volete un deposito più
ordinato, è normalmente consigliabile creare un nuovo clone. Fate
inoltre attenzione a manipolare direttamente il contenuto di +.git+: che
cosa succederebbe se un comando Git è in esecuzione allo stesso tempo, o
se se ci fosse un improvviso calo di corrente? In generale i refs
dovrebbero essere cancellati con *git update-ref -d*, anche se spesso
sembrerebbe sicuro cancella re +refs/original+ a mano.

=== Oggetti 'commit' ===

Abbiamo spiegato 2 dei 3 tipi di oggetto. Il terzo è l'oggetto
'commit'. Il suo contenuto dipende dal messaggio di commit, come anche
dalla data e l'ora in cui è stato creato. Perché far in maniera di
ottenere la stessa cosa dobbiamo fare 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 15:31:30 -0800"
     GIT_AUTHOR_NAME="Alice"
     GIT_AUTHOR_EMAIL="alice@example.com"
     GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
     GIT_COMMITTER_NAME="Bob"
     GIT_COMMITTER_EMAIL="bob@example.com"'  # Ritocco della data di creazione e degli autori
 $ find .git/objects -type f

Dovreste ora vedere il file +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+
che è il codice hash SHA1 del suo contenuto:

 "commit 158" NUL
 "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF
 "author Alice <alice@example.com> 1234567890 -0800" LF
 "committer Bob <bob@example.com> 1234567890 -0800" LF
 LF
 "Shakespeare" LF

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

Questo è il primo commi, non ci sono quindi commit genitori. Ma i commit
seguenti conterranno sempre almeno una linea che identifica un commit
genitore.

=== Indistinguibile dalla magia ===

I segreti di Git sembrano troppo semplici. Sembra che basterebbe
mescolare assieme qualche script shell e aggiungere un pizzico di codice
C per preparare un sistema del genere in qualche ora: una combinazione
di operazioni di filesystem di base e hashing SHA1, guarnito con
lockfile e file di sincronizzazione per avere un po' di robustezza.
Infatti questaè un descrizione accurata le prime versioni di Git.
Malgrado ciò, a parte qualche astuta tecnica di compressione per
risparmiare spazio e di indicizzazione per risparmiare tempo, ora
sappiamo come Git cambia abilmente un sistema di file in un perfetto
database per il controllo di versione.

Ad esempio, se un file nel database degli oggetti è corrotto da un errore
sul disco i codici hash non corrisponderanno più e verremo informati del
problema. Calcolando il codice hash del codice hash di altri oggetti è
possibile garantire integrità a tutti i livelli. I commit sono atomici,
nel senso che un commit non può memorizzare modifiche parziali: possiamo
calcolare il codice hash di un commit e salvarlo in un database dopo
aver creato i relativi oggetti 'tree', 'blob' e 'commit'. Il database
degli oggetti è immune da interruzioni inaspettate dovute ad esempio a
cali di corrente.

Possiamo anche far fronte ai tentativi di attacco più maliziosi.
Supponiamo ad esempio che un avversario tenti di modificare di nascosto il
contenuto di un file in una vecchia versione di un progetto. Per rendere
il database degli oggetti coerente, il nostro avversario deve anche
modificare il codice hash dei corrispondenti oggetti blob, visto che ora
sarà una stringa di byte diversa. Questo significa che dovrà cambiare il
codice hash di tutti gli oggetti tree che fanno riferimento al file, e
di conseguenza cambiare l'hash di tutti gli oggetti commit in ognuno di
questi tree, oltre ai codici hash di tutti i discendenti di questi
commit. Questo implica che il codice hash dell'HEAD ufficiale differirà
da quello del deposito corrotto. Seguendo la traccia di codici hash
erronei possiamo localizzare con precisione il file corrotto, come anche
il primo commit ad averlo introdotto.

In conclusione, purché i 20 byte che rappresentano l'ultimo commit sono
al sicuro, è impossibile manomettere il deposito Git.

Che dire delle famose funzionalità di Git? Della creazione di branch?
Dei merge? Delle tag? Semplici dettagli. L'HEAD corrente è conservata
nel file +.git/HEAD+ che contiene un codice hash di un oggetto commit.
Il codice hash viene aggiornato durante un commit e l'esecuzione di
molti altri comandi. Le branch funzionano in maniera molto simile: sono
file in +.git/refs/heads+. La stessa cosa vale per le tag, salvate in
+.git/refs/tags+ ma sono aggiornate da un insieme diverso di comandi.