hgbook

annotate it/ch04-concepts.xml @ 738:0edc45e721d5

First literal translation of Ch.4.
author Giulio@puck
date Wed Jul 01 17:25:39 2009 +0200 (2009-07-01)
parents
children 242567e04489
rev   line source
Giulio@738 1 <chapter id="chap:concepts">
Giulio@738 2 <?dbhtml filename="dietro-le-quinte.html"?>
Giulio@738 3 <title>Dietro le quinte</title>
Giulio@738 4
Giulio@738 5 <para id="x_2e8">Diversamente da molti sistemi di controllo di revisione, i concetti su cui Mercurial è costruito sono abbastanza semplici che è facile capire come il software funziona realmente. Conoscere questi dettagli non è certamente necessario, quindi è certamente sicuro saltare questo capitolo. Tuttavia, penso che otterrete di più dal software con un <quote>modello mentale</quote> di quello che sta succedendo.</para>
Giulio@738 6
Giulio@738 7 <para id="x_2e9">Essere in grado di capire quello che accade dietro le quinte mi dà la confidenza che Mercurial sia stato attentamente progettato per essere sia <emphasis>sicuro</emphasis> che <emphasis>efficiente</emphasis>. E allo stesso modo è importante, se è facile per me tenere a mente una buona idea di quello che il software sta facendo mentre effettuo un'attività di controllo di revisione, è meno probabile che venga sorpreso dal suo comportamento.</para>
Giulio@738 8
Giulio@738 9 <para id="x_2ea">In questo capitolo, tratteremo inizialmente i concetti chiave dietro alla progettazione di Mercurial, poi continueremo discutendo alcuni dei dettagli più interessanti della sua implementazione.</para>
Giulio@738 10
Giulio@738 11 <sect1>
Giulio@738 12 <title>La registrazione della cronologia di Mercurial</title>
Giulio@738 13
Giulio@738 14 <sect2>
Giulio@738 15 <title>Tenere traccia della cronologia di un singolo file</title>
Giulio@738 16
Giulio@738 17 <para id="x_2eb">Quando Mercurial tiene traccia delle modifiche a un file, memorizza la cronologia di quel file in un oggetto di metadati chiamato <emphasis>filelog</emphasis> (letteralmente, registro del file). Ogni voce in un filelog contiene informazioni sufficienti a ricostruire una revisione del file di cui viene tenuta traccia. I filelog sono memorizzati come file nella directory <filename role="special" class="directory">.hg/store/data</filename>. Un filelog contiene due tipi di informazione: dati di revisione e un indice per aiutare Mercurial a trovare una revisione in maniera efficiente.</para>
Giulio@738 18
Giulio@738 19 <para id="x_2ec">Il filelog di un file che sia di grandi dimensioni o abbia una lunga cronologia viene memorizzato in due file separati per i dati (con un suffisso <quote><literal>.d</literal></quote>) e l'indice (con un suffisso <quote><literal>.i</literal></quote>). Per file di piccole dimensioni senza molta cronologia, i dati di revisione e l'indice vengono combinati in un singolo file <quote><literal>.i</literal></quote>. La corrispondenza tra un file nella directory di lavoro e il filelog che tiene traccia della sua cronologia nel repository è illustrata nella <xref
Giulio@738 20 linkend="fig:concepts:filelog"/>.</para>
Giulio@738 21
Giulio@738 22 <figure id="fig:concepts:filelog">
Giulio@738 23 <title>Relazioni tra i file nella directory di lavoro e i filelog nel repository</title>
Giulio@738 24 <mediaobject>
Giulio@738 25 <imageobject><imagedata fileref="figs/filelog.png"/></imageobject>
Giulio@738 26 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 27 </mediaobject>
Giulio@738 28 </figure>
Giulio@738 29
Giulio@738 30 </sect2>
Giulio@738 31 <sect2>
Giulio@738 32 <title>Gestire i file tracciati</title>
Giulio@738 33
Giulio@738 34 <para id="x_2ee">Mercurial usa una struttura chiamata <emphasis>manifest</emphasis> per collezionare informazioni sui file di cui tiene traccia. Ogni voce nel manifest contiene informaizoni sui file presenti in un singolo changeset. Una voce registra quali file sono presenti nel changeset, la revisione di ogni file e alcuni altri frammenti di metadati sui file.</para>
Giulio@738 35
Giulio@738 36 </sect2>
Giulio@738 37 <sect2>
Giulio@738 38 <title>Registrare le informazioni di changeset</title>
Giulio@738 39
Giulio@738 40 <para id="x_2ef">Il <emphasis>changelog</emphasis> (letteralmente, registro dei cambiamenti) contiene informazioni su tutti i changeset. Ogni revisione registra chi ha inserito un cambiamento, il commento del changeset, altri frammenti di informazione relativi al changeset e la revisione del manifest da usare.</para>
Giulio@738 41
Giulio@738 42 </sect2>
Giulio@738 43 <sect2>
Giulio@738 44 <title>Relazioni tra le revisioni</title>
Giulio@738 45
Giulio@738 46 <para id="x_2f0">Nell'ambito di un changelog, di un manifest, o di un filelog, ogni revisione memorizza un puntatore al suo genitore diretto (o ai suoi due genitori, se è una revisione di unione). Come ho già detto, esistono anche relazioni tra revisioni <emphasis>attraverso</emphasis> queste strutture, ed esse hanno natura gerarchica.</para>
Giulio@738 47
Giulio@738 48 <para id="x_2f1">Per ogni changeset nel repository, esiste esattamente una revisione memorizzata nel changelog. Ogni revisione del changelog contiene un puntatore a una singola revisione del manifest. Una revisione del manifest memorizza un puntatore a una singola revisione di ogni filelog tracciato quando il changeset è stato creato. Queste relazioni sono illustrate nella <xref linkend="fig:concepts:metadata"/>.</para>
Giulio@738 49
Giulio@738 50 <figure id="fig:concepts:metadata">
Giulio@738 51 <title>Relazioni tra i metadati</title>
Giulio@738 52 <mediaobject>
Giulio@738 53 <imageobject><imagedata fileref="figs/metadata.png"/></imageobject>
Giulio@738 54 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 55 </mediaobject>
Giulio@738 56 </figure>
Giulio@738 57
Giulio@738 58 <para id="x_2f3">Come mostrato in figura, <emphasis>non</emphasis> c'è una relazione <quote>uno a uno</quote> tra le revisioni nel changelog, nel manifest, o nel filelog. Se un file tracciato da Mercurial non è cambiato tra due changeset, la voce per quel file nelle due revisioni del manifest punterà alla stessa revisione nel suo filelog<footnote>
Giulio@738 59 <para id="x_725">&Egrave; possibile (sebbene inusuale) che il manifest rimanga lo stesso tra due changeset, nel qual caso le voci del changelog per quei changeset punteranno alla stessa revisione del manifest.</para>
Giulio@738 60 </footnote>.</para>
Giulio@738 61
Giulio@738 62 </sect2>
Giulio@738 63 </sect1>
Giulio@738 64 <sect1>
Giulio@738 65 <title>Memorizzazione sicura ed efficiente</title>
Giulio@738 66
Giulio@738 67 <para id="x_2f4">I puntelli dei changelog, dei manifest e dei filelog sono forniti da una singola struttura chiamata <emphasis>revlog</emphasis> (letteralmente, registro di revisione).</para>
Giulio@738 68
Giulio@738 69 <sect2>
Giulio@738 70 <title>Memorizzazione efficiente</title>
Giulio@738 71
Giulio@738 72 <para id="x_2f5">Il revlog fornisce una memorizzazione efficiente delle revisioni usando un meccanismo di <emphasis>delta</emphasis>. Invece di memorizzare una copia completa di un file per ogni revisione, memorizza i cambiamenti necessari a trasformare una revisione più vecchia nella nuova revisione. Per molti tipi di dati di file, queste delta sono tipicamente una frazione percentuale della dimensione di un'intera copia di un file.</para>
Giulio@738 73
Giulio@738 74 <para id="x_2f6">Alcuni sistemi di controllo di revisione obsoleti possono lavorare solo con le delta di file di testo. Essi devono memorizzare file binari come fotografie complete o codificarli in una rappresentazione testuale, entrambi approcci dispendiosi. Mercurial può maneggiare in maniera efficiente le delta di file con contenuti binari arbitrari; non ha bisogno di trattare il testo in maniera speciale.</para>
Giulio@738 75
Giulio@738 76 </sect2>
Giulio@738 77 <sect2 id="sec:concepts:txn">
Giulio@738 78 <title>Operazioni sicure</title>
Giulio@738 79
Giulio@738 80 <para id="x_2f7">Mercurial si limita ad <emphasis>aggiungere</emphasis> dati alla fine di un file di revlog. Non modifica mai una sezione di un file dopo che lo ha scritto. Questo è sia più robusto che più efficiente rispetto a schemi che hanno bisogno di modificare o riscrivere i dati.</para>
Giulio@738 81
Giulio@738 82 <para id="x_2f8">In più, Mercurial tratta ogni scrittura come parte di una <emphasis>transazione</emphasis> che può coinvolgere un qualsiasi numero di file. Una transazione è <emphasis>atomica</emphasis>: o l'intera transazione ha successo e i suoi effetti sono visibili in lettura in un unico passo, oppure l'intera operazione viene annullata. Questa garanzia di atomicità significa che se state eseguendo due copie di Mercurial, dove una sta leggendo dati e l'altra sta scrivendo, il lettore non vedrà mai un risultato parzialmente scritto che potrebbe confonderlo.</para>
Giulio@738 83
Giulio@738 84 <para id="x_2f9">Il fatto che Mercurial aggiunga solo ai file rende più facile fornire questa garanzia transazionale. Più è facile fare cose come queste, più dovreste essere fiduciosi che vengano fatte correttamente.</para>
Giulio@738 85
Giulio@738 86 </sect2>
Giulio@738 87 <sect2>
Giulio@738 88 <title>Reperimento veloce</title>
Giulio@738 89
Giulio@738 90 <para id="x_2fa">Mercurial evita astutamente un tranello comune a tutti i primi sistemi di controllo di revisione: il problema del <emphasis>reperimento inefficiente</emphasis>. La maggior parte dei sistemi di controllo di revisione memorizza i contenuti di una revisione come una serie incrementale di modifiche contro una <quote>fotografia</quote>. (Alcuni basano la fotografia sulla revisione più vecchia, altri su quella più nuova.) Per ricostruire una revisione specifica, dovete prima leggere la fotografia, e poi ognuna delle revisioni tra la fotografia e la vostra revisione obiettivo. Più un file accumula cronologia, più revisioni dovete leggere, quindi più tempo viene impiegato per ricostruire una particolare revisione.</para>
Giulio@738 91
Giulio@738 92 <figure id="fig:concepts:snapshot">
Giulio@738 93 <title>Fotografia di un revlog, con delta incrementali</title>
Giulio@738 94 <mediaobject>
Giulio@738 95 <imageobject><imagedata fileref="figs/snapshot.png"/></imageobject>
Giulio@738 96 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 97 </mediaobject>
Giulio@738 98 </figure>
Giulio@738 99
Giulio@738 100 <para id="x_2fc">L'innovazione che Mercurial applica a questo problema è semplice ma efficace. Una volta che la quantità totale di informazioni di delta memorizzata dall'ultima immagine supera una soglia fissata, Mercurial memorizza una nuova immagine (compressa, naturalmente) invece di un'altra delta. Questo rende possibile ricostruire velocemente <emphasis>qualsiasi</emphasis> revisione di un file. Questo approccio funziona così bene che in seguito è stato copiato da molti altri sistemi di controllo di revisione.</para>
Giulio@738 101
Giulio@738 102 <para id="x_2fd">La <xref linkend="fig:concepts:snapshot"/> illustra l'idea. In una voce di un file indice per un revlog, Mercurial memorizza l'intervallo di voci dal file di dati che deve leggere per ricostruire una particolare revisione.</para>
Giulio@738 103
Giulio@738 104 <sect3>
Giulio@738 105 <title>Digressione: l'influenza della compressione video</title>
Giulio@738 106
Giulio@738 107 <para id="x_2fe">Se avete familiarità con la compressione video o avete mai guardato un segnale televisivo attraverso un cavo digitale o un servizio satellitare, potreste sapere che la maggior parte degli schemi per la compressione video memorizzano ogni frame del video come una delta rispetto al frame precedente.</para>
Giulio@738 108
Giulio@738 109 <para id="x_2ff">Mercurial prende in prestito questa idea per fare in modo che sia possibile ricostruire una revisione da una fotografia e un piccolo numero di delta.</para>
Giulio@738 110
Giulio@738 111 </sect3>
Giulio@738 112 </sect2>
Giulio@738 113 <sect2>
Giulio@738 114 <title>Identificazione e integrità forte</title>
Giulio@738 115
Giulio@738 116 <para id="x_300">Insieme alle informazioni di delta o di fotografia, una voce di revlog contiene un hash crittografico dei dati che rappresenta. Questo rende difficile contraffarre i contenuti di una revisione e facile scoprire una accidentale corruzione.</para>
Giulio@738 117
Giulio@738 118 <para id="x_301">Gli hash forniscono più di un semplice controllo contro la corruzione; essi sono usati come identificatori per le revisioni. Gli hash di identificazione dei changeset che avete visto come utenti finali provengono dalle revisioni del changelog. Sebbene anche i filelog e il manifest facciano uso di hash, in questo caso Mercurial li impiega solo dietro le quinte.</para>
Giulio@738 119
Giulio@738 120 <para id="x_302">Mercurial verifica che gli hash siano corretti quando reperisce le revisioni dei file e quando estrae i cambiamenti da un altro repository. Se incontra un problema di integrità, lo segnalerà e bloccherà l'operazione che stava eseguendo.</para>
Giulio@738 121
Giulio@738 122 <para id="x_303">In aggiunta all'effetto che ha sull'efficienza del reperimento, l'uso di fotografie periodiche da parte di Mercurial rende i repository più robusti nei confronti della corruzione parziale dei dati. Se un revlog diventa parzialmente corrotto a causa di un errore hardware o di un bug di sistema, spesso rimane possibile ricostruire alcune o la maggior parte delle revisioni a partire dalle sezioni non corrotte del revlog che si trovano prima e dopo la sezione rovinata. Questo non sarebbe possibile con un modello di memorizzazione basato unicamente sulle delta.</para>
Giulio@738 123 </sect2>
Giulio@738 124 </sect1>
Giulio@738 125
Giulio@738 126 <sect1>
Giulio@738 127 <title>Cronologia delle revisioni, ramificazioni e unioni</title>
Giulio@738 128
Giulio@738 129 <para id="x_304">Ogni voce in un revlog di Mercurial conosce l'identità della propria revisione progenitrice diretta, di solito chiamata <emphasis>genitore</emphasis>. In effetti, una revisione contiene spazio non solo per un genitore, ma per due. Mercurial usa un hash speciale, chiamato <quote>identificatore nullo</quote>, per rappresentare l'idea <quote>non c'è alcun genitore qui</quote>. Questo hash è semplicemente una stringa di zero.</para>
Giulio@738 130
Giulio@738 131 <para id="x_305">Nella <xref linkend="fig:concepts:revlog"/>, potete vedere un esempio della struttura concettuale di un revlog. I filelog, i manifest e i changelog hanno tutti questa identica struttura; essi differiscono solo per il tipo di dati memorizzati in ogni delta e fotografia.</para>
Giulio@738 132
Giulio@738 133 <para id="x_306">La prima revisione in un revlog (nella parte inferiore dell'immagine) presenta un identificatore nullo in entrambi i posti genitoriali. Per una revisione <quote>normale</quote>, il primo posto genitoriale contiene l'identificatore della revisione genitore e il secondo contiene l'identificatore nullo, indicando che la revisione possiede un solo vero genitore. Due revisioni qualsiasi che possiedano lo stesso identificatore di genitore sono rami. Una revisione che rappresenta un'unione tra rami ha due identificatori di revisione normali nei propri posti genitoriali.</para>
Giulio@738 134
Giulio@738 135 <figure id="fig:concepts:revlog">
Giulio@738 136 <title>La struttura concettuale di un revlog</title>
Giulio@738 137 <mediaobject>
Giulio@738 138 <imageobject><imagedata fileref="figs/revlog.png"/></imageobject>
Giulio@738 139 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 140 </mediaobject>
Giulio@738 141 </figure>
Giulio@738 142
Giulio@738 143 </sect1>
Giulio@738 144 <sect1>
Giulio@738 145 <title>La directory di lavoro</title>
Giulio@738 146
Giulio@738 147 <para id="x_307">Nella directory di lavoro, Mercurial memorizza una fotografia dei file contenuti nel repository come un changeset particolare.</para>
Giulio@738 148
Giulio@738 149 <para id="x_308">La directory di lavoro <quote>sa</quote> quale changeset contiene. Quando aggiornate la directory di lavoro per contenere un particolare changeset, Mercurial cerca la revisione appropriata del manifest per trovare quali file stava tracciando nel momento in cui quel changeset è stato inserito, e quale revisione di ogni file era corrente in quel momento. Poi ricrea una copia di ognuno di quei file, con gli stessi contenuti che avevano quando il changeset è stato inserito.</para>
Giulio@738 150
Giulio@738 151 <para id="x_309">Il <emphasis>dirstate</emphasis> (letteralmente, stato della directory) è una struttura speciale che contiene le informazioni possedute da Mercurial sulla directory di lavoro. Viene mantenuto sotto forma di un file chiamato <filename>.hg/dirstate</filename> all'interno di un repository. Il dirstate contiene i dettagli dei changeset a cui la directory di lavoro è aggiornata, e di tutti i file che Mercurial sta tracciando nella directory di lavoro. Esso permette anche a Mercurial di notare velocemente i file modificati, registrando le loro date e dimensioni al momento del ~checkout~.</para>
Giulio@738 152
Giulio@738 153 <para id="x_30a">Esattamente come una revisione di un revlog ha spazio per due genitori, in modo da poter rappresentare sia una normale revisione (con un genitore) che un'unione di due revisioni precedenti, il dirstate ha spazio per due genitori. Quando usate il comando <command role="hg-cmd">hg update</command>, il changeset a cui aggiornate viene memorizzato nel posto del <quote>primo genitore</quote>, e l'identificatore nullo nel secondo. Quando incorporate un altro changeset tramite <command role="hg-cmd">hg merge</command>, il primo genitore rimane lo stesso, e il secondo genitore viene riempito con il changeset che state incorporando. Il comando <command role="hg-cmd">hg parents</command> vi dice quali sono i genitori del dirstate.</para>
Giulio@738 154
Giulio@738 155 <sect2>
Giulio@738 156 <title>Cosa succede quando eseguite un commit</title>
Giulio@738 157
Giulio@738 158 <para id="x_30b">Il dirstate memorizza le informazioni sui genitori per altri scopi oltre alla mera contabilità. Mercurial usa i genitori del dirstate come <emphasis>i genitori di un nuovo changeset</emphasis> quando effettuate un commit.</para>
Giulio@738 159
Giulio@738 160 <figure id="fig:concepts:wdir">
Giulio@738 161 <title>La directory di lavoro può avere due genitori</title>
Giulio@738 162 <mediaobject>
Giulio@738 163 <imageobject><imagedata fileref="figs/wdir.png"/></imageobject>
Giulio@738 164 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 165 </mediaobject>
Giulio@738 166 </figure>
Giulio@738 167
Giulio@738 168 <para id="x_30d">La <xref linkend="fig:concepts:wdir"/> mostra il normale stato della directory di lavoro, dove ha un singolo changeset come genitore. Quel changeset è la <emphasis>punta</emphasis>, il changeset più recente nel repository che non possiede figli.</para>
Giulio@738 169
Giulio@738 170 <figure id="fig:concepts:wdir-after-commit">
Giulio@738 171 <title>La directory di lavoro acquisisce nuovi genitori dopo un commit</title>
Giulio@738 172 <mediaobject>
Giulio@738 173 <imageobject><imagedata fileref="figs/wdir-after-commit.png"/></imageobject>
Giulio@738 174 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 175 </mediaobject>
Giulio@738 176 </figure>
Giulio@738 177
Giulio@738 178 <para id="x_30f">&Egrave; utile pensare alla directory di lavoro come al <quote>changeset che sto per inserire</quote>. Qualsiasi file che avete detto a Mercurial di avere aggiunto, rimosso, rinominato, o copiato verrà riflesso in quel changeset, così come le modifiche a qualsiasi file che Mercurial sta già tracciando; il nuovo changeset acquisirà i genitori della directory di lavoro come i propri.</para>
Giulio@738 179
Giulio@738 180 <para id="x_310">Dopo un commit, Mercurial aggiornerà i genitori della directory di lavoro, in modo che il primo genitore sia l'identificatore del nuovo changeset e il secondo sia l'identificatore nullo. Questo viene mostrato nella <xref linkend="fig:concepts:wdir-after-commit"/>. Mercurial non tocca alcun file nella directory di lavoro quando eseguite un commit, ma si limita a modificare il dirstate per annotare i suoi nuovi genitori.</para>
Giulio@738 181
Giulio@738 182 </sect2>
Giulio@738 183 <sect2>
Giulio@738 184 <title>Creare una nuova testa</title>
Giulio@738 185
Giulio@738 186 <para id="x_311">&Egrave; perfettamente normale aggiornare la directory di lavoro a un changeset diverso dalla punta corrente. Per esempio, potreste voler sapere come il vostro progetto appariva lo scorso martedì, oppure potreste stare guardando attraverso i changeset per vedere quale ha introdotto un bug. In casi come questo, la cosa naturale da fare è aggiornare la directory di lavoro al changeset che vi interessa e poi esaminare i file direttamente nella directory di lavoro per vedere com'erano i loro contenuti quando avevate inserito quel changeset. Gli effetti di questa azione si possono vedere nella <xref linkend="fig:concepts:wdir-pre-branch"/>.</para>
Giulio@738 187
Giulio@738 188 <figure id="fig:concepts:wdir-pre-branch">
Giulio@738 189 <title>La directory di lavoro, aggiornata a un vecchio changeset</title>
Giulio@738 190 <mediaobject>
Giulio@738 191 <imageobject><imagedata fileref="figs/wdir-pre-branch.png"/></imageobject>
Giulio@738 192 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 193 </mediaobject>
Giulio@738 194 </figure>
Giulio@738 195
Giulio@738 196 <para id="x_313">Avendo aggiornato la directory di lavoro a un vecchio changeset, cosa succede se apportate alcuni cambiamenti e poi li inserite? Mercurial si comporta nello stesso modo delineato in precedenza. I genitori della directory di lavoro diventano i genitori del nuovo changeset. Questo nuovo changeset non ha figli, quindi diventa la nuova punta. E il repository ora contiene due changeset che non hanno figli, che vengono chiamati <emphasis>teste</emphasis>. Potete vedere la struttura creata da questa operazione nella <xref linkend="fig:concepts:wdir-branch"/>.</para>
Giulio@738 197
Giulio@738 198 <figure id="fig:concepts:wdir-branch">
Giulio@738 199 <title>La situazione dopo un commit effettuato su un aggiornamento a un vecchio changeset</title>
Giulio@738 200 <mediaobject>
Giulio@738 201 <imageobject><imagedata fileref="figs/wdir-branch.png"/></imageobject>
Giulio@738 202 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 203 </mediaobject>
Giulio@738 204 </figure>
Giulio@738 205
Giulio@738 206 <note>
Giulio@738 207 <para id="x_315">Se Mercurial vi è nuovo, dovreste tenere a mente un <quote>errore</quote> comune, che è quello di usare il comando <command role="hg-cmd">hg pull</command> senza alcuna opzione. Di default, il comando <command role="hg-cmd">hg pull</command> <emphasis>non</emphasis> aggiorna la directory di lavoro, così propagherete nuovi cambiamenti nel vostro repository, ma la directory di lavoro rimarrà sincronizzata allo stesso changeset in cui era prima della propagazione. Se ora effettuate alcuni cambiamenti e poi li inserite, creerete una nuova testa, perché la vostra directory di lavoro non è stata sincronizzata a qualunque revisione fosse la punta in quel momento. Per combinare le operazioni di estrazione e aggiornamento, eseguite <command>hg pull -u</command>.</para>
Giulio@738 208
Giulio@738 209 <para id="x_316">Ho messo la parola <quote>errore</quote> tra virgolette perché tutto quello che dovete fare per rettificare la situazione in cui avete creato una nuova testa per sbaglio è eseguire il comando <command role="hg-cmd">hg merge</command> seguito da <command role="hg-cmd">hg commit</command>. In altre parole, questo errore non ha quasi mai conseguenze negative; è solo qualcosa che può sorprendere gli utenti alle prime armi. Più tardi, discuterò altri modi per evitare questo comportamento, e le ragioni per cui Mercurial si comporta in questo modo inizialmente sorprendente.</para>
Giulio@738 210 </note>
Giulio@738 211
Giulio@738 212 </sect2>
Giulio@738 213 <sect2>
Giulio@738 214 <title>Unire i cambiamenti</title>
Giulio@738 215
Giulio@738 216 <para id="x_317">Quando eseguite il comando <command role="hg-cmd">hg merge</command>, Mercurial lascia intonso il primo genitore della directory di lavoro e imposta il secondo genitore al cambiamento che state incorporando, come mostrato nella <xref linkend="fig:concepts:wdir-merge"/>.</para>
Giulio@738 217
Giulio@738 218 <figure id="fig:concepts:wdir-merge">
Giulio@738 219 <title>Unire due teste</title>
Giulio@738 220 <mediaobject>
Giulio@738 221 <imageobject>
Giulio@738 222 <imagedata fileref="figs/wdir-merge.png"/>
Giulio@738 223 </imageobject>
Giulio@738 224 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@738 225 </mediaobject>
Giulio@738 226 </figure>
Giulio@738 227
Giulio@738 228 <para id="x_319">Mercurial deve anche modificare la directory di lavoro, per unire i file gestiti dai due changeset. Semplificando un poco, il processo di unione funziona in questo modo, per ogni file contenuto nei manifest di entrambi i changeset.</para>
Giulio@738 229 <itemizedlist>
Giulio@738 230 <listitem><para id="x_31a">Se nessuno dei changeset ha modificato il file, non fare nulla con quel file.</para>
Giulio@738 231 </listitem>
Giulio@738 232 <listitem><para id="x_31b">Se un changeset ha modificato il file, e l'altro non lo ha modificato, crea la copia modificata del file nella directory di lavoro.</para>
Giulio@738 233 </listitem>
Giulio@738 234 <listitem><para id="x_31c">Se un changeset ha rimosso un file, e l'altro no (o anche l'altro lo ha cancellato), cancella il file dalla directory di lavoro.</para>
Giulio@738 235 </listitem>
Giulio@738 236 <listitem><para id="x_31d">Se un changeset ha cancellato un file, ma l'altro lo ha modificato, chiedi all'utente cosa vuole fare: tenere il file modificato oppure rimuoverlo?</para>
Giulio@738 237 </listitem>
Giulio@738 238 <listitem><para id="x_31e">Se entrambi i changeset hanno modificato un file, richiama un programma di unione esterno per scegliere i contenuti del file da unire. Questo potrebbe richiedere informazioni da parte dell'utente.</para>
Giulio@738 239 </listitem>
Giulio@738 240 <listitem><para id="x_31f">Se un changeset ha modificato un file, e l'altro ha rinominato o copiato il file, assicurati che i cambiamenti seguano il nuovo nome del file.</para>
Giulio@738 241 </listitem></itemizedlist>
Giulio@738 242 <para id="x_320">L'operazione ha molti altri dettagli&emdash;le unioni sono piene di casi particolari&emdash;ma queste sono le scelte più comuni coinvolte nel processo di unione. Come potete vedere, la maggior parte dei casi sono completamente automatizzati, e in effetti la maggior parte delle unioni termina automaticamente senza richiedere il vostro intervento per risolvere alcun conflitto.</para>
Giulio@738 243
Giulio@738 244 <para id="x_321">Se pensate a quello che succede quando effettuate un commit dopo un'unione, ancora una volta la directory di lavoro è <quote>il changeset che state per inserire</quote>. Dopo che il comando <command role="hg-cmd">hg merge</command> ha terminato, la directory di lavoro possiede due genitori; questi diventeranno i genitori del nuovo changeset.</para>
Giulio@738 245
Giulio@738 246 <para id="x_322">Mercurial vi lascia effettuare molteplici unioni, ma dovete inserire i risultati di ogni singola unione man mano che procedete. Questo è necessario perché Mercurial tiene traccia solamente di due genitori sia per le revisioni che per la directory di lavoro. Mentre sarebbe tecnicamente fattibile unire molteplici changeset alla volta, Mercurial evita di farlo per semplicità. Con unioni a più vie, il rischio di confondere l'utente, di incappare in conflitti sgradevoli da risolvere e di fare una terribile confusione in un'unione diventerebbe intollerabile.</para>
Giulio@738 247
Giulio@738 248 </sect2>
Giulio@738 249
Giulio@738 250 <sect2>
Giulio@738 251 <title>Le unioni e i cambiamenti di nome</title>
Giulio@738 252
Giulio@738 253 <para id="x_69a">Un numero sorprendente di sistemi di controllo di revisione dedica poca o addirittura nessuna attenzione al <emphasis>nome</emphasis> di un file ~over time~. Per esempio, era pratica comune che se un file venisse rinominato in una delle due parti di un'unione, i cambiamenti dell'altra parte sarebbero stati silenziosamente scartati.</para>
Giulio@738 254
Giulio@738 255 <para id="x_69b">Mercurial registra metadati quando gli dite di effettuare una cambiamento di nome o una copia. Usa questi metadati durante le unioni per comportarsi in maniera appropriata. Per esempio, se modifico il nome di un file, e voi lo modificate senza rinominarlo, quando uniamo i nostri cambiamenti il file verrà rinominato e gli verranno applicate le vostre modifiche.</para>
Giulio@738 256 </sect2>
Giulio@738 257 </sect1>
Giulio@738 258
Giulio@738 259 <sect1>
Giulio@738 260 <title>Altre caratteristiche di progettazione interessanti</title>
Giulio@738 261
Giulio@738 262 <para id="x_323">Nelle sezioni precedenti, ho provato a evidenziare alcuni degli aspetti più importanti nella progettazione di Mercurial, per illustrare come dedichi la dovuta attenzione a prestazioni e affidabilità. Tuttavia, l'attenzione ai dettagli non finisce lì. Ci sono un certo numero di altri aspetti nella costruzione di Mercurial che trovo personalmente interessanti. Ne dettaglierò alcuni qui, separatamente dagli elementi ~<quote>big ticket</quote>~ analizzati finora, in modo che se siete interessati potete farvi un'idea più precisa della quantità di riflessioni che serve per realizzare un sistema ben progettato.</para>
Giulio@738 263
Giulio@738 264 <sect2>
Giulio@738 265 <title>Compressione intelligente</title>
Giulio@738 266
Giulio@738 267 <para id="x_324">Quando è appropriato, Mercurial memorizzera sia la fotografia che le delta in forma compressa. Fa questo <emphasis>cercando</emphasis> sempre di comprimere una fotografia o una delta, ma memorizzando la versione compressa solo se è più piccola della versione originale.</para>
Giulio@738 268
Giulio@738 269 <para id="x_325">Questo significa che Mercurial fa <quote>la cosa giusta</quote> quando memorizza un file il cui formato sia già compresso, come un archivio <literal>zip</literal> o un'immagine JPEG. Quando questi tipi di file sono compressi una seconda volta, il file risultante è tipicamente più grande del file originale, così Mercurial memorizzerà la prima versione semplice del file <literal>zip</literal> o <literal>JPEG</literal>.</para>
Giulio@738 270
Giulio@738 271 <para id="x_326">Le delta tra le revisioni di un file compresso sono di solito più grandi delle fotografie del file, e Mercurial ancora una volta fa <quote>la cosa giusta</quote> in questi casi. Scopre che tale delta oltrepassa la soglia alla quale dovrebbe memorizzare una fotografia completa del file, quindi memorizza la fotografia, risparmiando ancora spazio nei confronti di un approccio ingenuo basato solo sulle delta.</para>
Giulio@738 272
Giulio@738 273 <sect3>
Giulio@738 274 <title>Ricompressione di rete</title>
Giulio@738 275
Giulio@738 276 <para id="x_327">Nel memorizzare le revisioni su disco, Mercurial usa l'algoritmo di compressione <quote>deflate</quote> (lo stesso usato dal popolare formato <literal>zip</literal>), che bilancia una buona velocità con un rispettabile rapporto di compressione. Tuttavia, quando trasmette i dati di una revisione attraverso una connessione di rete, Mercurial decomprime i dati di revisione compressi.</para>
Giulio@738 277
Giulio@738 278 <para id="x_328">Se la connessione usa il protocollo HTTP, Mercurial ricomprime l'intero flusso di dati usando un algoritmo di compressione che ha un rapporto di compressione migliore (l'algoritmo Burrows-Wheeler del pacchetto di compressione <literal>bzip2</literal> largamente utilizzato). Questa combinazinoe di algoritmo e compressione dell'intero flusso (invece di una revisione alla volta) riduce sostanzialmente il numero di byte da trasferire, producendo prestazioni di rete migliori sulla maggior parte delle reti.</para>
Giulio@738 279
Giulio@738 280 <para id="x_329">Se la connessione usa <command>ssh</command>, Mercurial <emphasis>non</emphasis> ricomprime il flusso, perché <command>ssh</command> è già in grado di farlo da solo. Potete dire a Mercurial di usare sempre le funzionalità di compressione di <command>ssh</command> modificando il file <filename>.hgrc</filename> che si trova nella vostra directory personale nel modo seguente.</para>
Giulio@738 281
Giulio@738 282 <programlisting>[ui]
Giulio@738 283 ssh = ssh -C</programlisting>
Giulio@738 284
Giulio@738 285 </sect3>
Giulio@738 286 </sect2>
Giulio@738 287 <sect2>
Giulio@738 288 <title>Ordinamento e atomicità delle operazioni di lettura e scrittura</title>
Giulio@738 289
Giulio@738 290 <para id="x_32a">Aggiungere in coda a un file non è tutta la storia quando si cerca di garantire che una lettura non veda scritture parziali. Se ricordate la <xref linkend="fig:concepts:metadata"/>, le revisioni in un changelog puntano alle revisioni nel manifest, e le revisioni nel manifest puntano alle revisioni nel filelog. Questa gerarchia è intenzionale.</para>
Giulio@738 291
Giulio@738 292 <para id="x_32b">Una operazione di scrittura avvia una transazione modificando i dati nel filelog e nel manifest, e non modifica alcun dato contenuto nel changelog fino a quando quelli hanno terminato. Una operazione di lettura comincia leggendo i dati nel changelog, poi i dati nel manifest, seguiti dai dati nel filelog.</para>
Giulio@738 293
Giulio@738 294 <para id="x_32c">Dato che la scrittura ha sempre terminato di modificare i dati nel filelog e nel manifest prima di modificare il changelog, una lettura non vedrà mai un puntatore dal changelog verso una revisione parzialmente modificata nel manifest, e non vedrà mai un puntatore dal manifest verso a una revisione parzialmente modificata nel filelog.</para>
Giulio@738 295
Giulio@738 296 </sect2>
Giulio@738 297 <sect2>
Giulio@738 298 <title>Accesso concorrente</title>
Giulio@738 299
Giulio@738 300 <para id="x_32d">Le garanzie sull'ordinamento e sull'atomicità delle operazioni di lettura significano che Mercurial non avrà mai bisogno di <emphasis>bloccare</emphasis> un repository di cui sta leggendo i dati, anche se il repository sta venendo modificato mentre la lettura sta accadendo. Questo ha un importante effetto sulla scalabilità; potete avere un numero arbitrario di processi Mercurial che in sicurezza leggono contemporaneamente i dati da un repository, senza preoccuparvi che stia venendo modificato oppure no.</para>
Giulio@738 301
Giulio@738 302 <para id="x_32e">La natura delle letture prive di blocchi significa che se state condividendo un repository su un sistema multi-utente, non avete bisogno di concedere ad altri utenti locali i permessi di <emphasis>scrittura</emphasis> al vostro repository per renderli in grado di clonarlo o estrarne i cambiamenti; essi hanno bisogno solo dei permessi di <emphasis>lettura</emphasis>. (Questa <emphasis>non</emphasis> è una caratteristica comune tra i sistemi di controllo di revisione, quindi non datela per scontata! La maggior parte dei sistemi richiede che i lettori siano in grado di bloccare un repository per accederlo in sicurezza, cosa che naturalmente provoca tutti i tipi di sgradevoli e fastidiosi problemi di sicurezza e amministrazione.)</para>
Giulio@738 303
Giulio@738 304 <para id="x_32f">Mercurial usa i blocchi per assicurarsi che un solo processo alla volta possa effettuare modifiche a un repository (il meccanismo di bloccaggio è sicuro persino su file system che sono notoriamente ostili ai blocchi, come NFS). Se un repository è blocato, una operazione di scrittura aspetterà per qualche tempo prima di riprovare se il repository si è sbloccato, ma se il repository rimane bloccato troppo a lungo, dopo un po' il processo che sta tentando di scrivere ~will time out~. Questo significa che il vostri script automatici non rimarranno bloccati per sempre accumulandosi se un sistema dovesse innavvertitamente cadere, per esempio. (Sì, il valore del timeout è configurabile, da zero a infinito.)</para>
Giulio@738 305
Giulio@738 306 <sect3>
Giulio@738 307 <title>Accesso sicuro al dirstate</title>
Giulio@738 308
Giulio@738 309 <para id="x_330">Come con i dati di revisione, Mercurial non impiega un blocco per leggere il file di dirstate; acquisisce un blocco per modificarlo. Per evitare la possibilità di leggere una copia parzialmente modificata di un file di dirstate, Mercurial scrive su un file con un nome unico nella stessa directory del file di dirstate, poi cambia il nome del file temporaneo a <filename>dirstate</filename> in maniera atomica. Il file chiamato <filename>dirstate</filename> quindi è garantito di essere completo, non parzialmente modificato.</para>
Giulio@738 310
Giulio@738 311 </sect3>
Giulio@738 312 </sect2>
Giulio@738 313 <sect2>
Giulio@738 314 <title>Evitare le operazioni di seek</title>
Giulio@738 315
Giulio@738 316 <para id="x_331">Un aspetto critico delle prestazioni di Mercurial è quello di evitare le operazioni di seek della testina del disco, dato che ognuna di queste operazioni è molto più dispendiosa persino di una operazione di lettura relativamente grande.</para>
Giulio@738 317
Giulio@738 318 <para id="x_332">Questa è la ragione per cui, per esempio, il dirstate è memorizzato in un singolo file. Se ci fosse un file di dirstate per ogni directory che Mercurial traccia, il disco effettuerebbe un'operazione di seek una volta per directory. Invece, Mercurial legge l'intero file di dirstate in un singolo passo.</para>
Giulio@738 319
Giulio@738 320 <para id="x_333">Mercurial adotta anche una strategia <quote>copy-on-write</quote> quando clona un repository su disco locale. Invece di copiare ogni file di revlog dal vecchio repository al nuovo, utilizza <quote>collegamenti fisici</quote> per indicare che <quote>due nomi puntano allo stesso file</quote>. Quando Mercurial sta per modificare uno dei file di revlog, controlla per vedere se il numero di nomi che puntano al file è più grande di uno. Se è così, questo significa che più di un repository sta usando il file, quindi Mercurial ne fa una nuova copia riservata a questo repository.</para>
Giulio@738 321
Giulio@738 322 <para id="x_334">Alcuni sviluppatori di sistemi per il controllo di revisione hanno fatto notare che questa idea di fare una copia privata completa di un file non è molto efficiente nel suo uso di spazio su disco. Sebbene sia vero, lo spazio su disco è economico, e questo metodo consente di avere le prestazioni migliori rinviando la maggior parte della contabilità al sistema operativo. Una strategia alternativa molto probabilmente ridurrebbe le prestazioni e aumenterebbe la complessità del software, ma velocità e semplicità sono aspetti chiave per la <quote>facilità</quote> nell'uso quotidiano.</para>
Giulio@738 323
Giulio@738 324 </sect2>
Giulio@738 325 <sect2>
Giulio@738 326 <title>Altre informazioni contenute nel dirstate</title>
Giulio@738 327
Giulio@738 328 <para id="x_335">Dato che Mercurial non vi obbliga a dirgli quando state modificando un file, usa il dirstate per memorizzare alcune informazioni aggiuntive in modo da poter determinare efficientemente se avete modificato un file. Per ogni file nella directory di lavoro, memorizza la data in cui lo ha modificato per l'ultima volta e la dimensione che il file aveva in quel momento.</para>
Giulio@738 329
Giulio@738 330 <para id="x_336">Quando utilizzate esplicitamente <command role="hg-cmd">hg add</command>, <command role="hg-cmd">hg remove</command>, <command role="hg-cmd">hg rename</command>, o <command role="hg-cmd">hg copy</command> su un file, Mercurial aggiorna il dirstate in modo che sappia cosa fare con quei file quando effettuate un commit.</para>
Giulio@738 331
Giulio@738 332 <para id="x_337">Il dirstate aiuta Mercurial a controllare in maniera efficiente lo stato dei file in un repository.</para>
Giulio@738 333
Giulio@738 334 <itemizedlist>
Giulio@738 335 <listitem>
Giulio@738 336 <para id="x_726">Quando Mercurial controlla lo stato di un file nella directory di lavoro, prima confronta la data dell'ultima modifica del file con la data registrata nel dirstate che indica quando Mercurial ha modificato quel file per l'ultima volta. Se le due date sono le stesse, il file non deve essere stato modificato, quindi Mercurial non ha bisogno di fare ulteriori controlli.</para>
Giulio@738 337 </listitem>
Giulio@738 338 <listitem>
Giulio@738 339 <para id="x_727">Se la dimensione del file è cambiata, il file deve essere stato modificato. Solo nel caso in cui la data di modifica sia cambiata, ma non la dimensione, Mercurial ha effettivamente bisogno di leggere i contenuti del file per vedere se è stato modificato.</para>
Giulio@738 340 </listitem>
Giulio@738 341 </itemizedlist>
Giulio@738 342
Giulio@738 343 <para id="x_728">Memorizzare le dimensioni e la data di ultima modifica riduce drammaticamente il numero di operazioni di lettura che Mercurial deve effettuare quando eseguiamo comandi come <command>hg status</command>. Questo risulta in ampi miglioramenti delle prestazioni.</para>
Giulio@738 344 </sect2>
Giulio@738 345 </sect1>
Giulio@738 346 </chapter>