hgbook

annotate it/ch09-undo.xml @ 759:fb05936ccde9

Minor modifications to Ch.9.
author Giulio@puck
date Sun Jul 19 01:14:57 2009 +0200 (2009-07-19)
parents e553aac061f1
children bd13bb9c9e03
rev   line source
Giulio@757 1 <chapter id="chap:undo">
Giulio@757 2 <?dbhtml filename="trovare-e-correggere-gli-errori.html"?>
Giulio@757 3 <title>Trovare e correggere gli errori</title>
Giulio@757 4
Giulio@757 5 <para id="x_d2">Sbagliare potrebbe essere umano, ma per gestire davvero bene le conseguenze degli errori ci vuole un sistema di controllo di revisione di prima qualità. In questo capitolo, discuteremo alcune tecniche che potete usare quando scoprite che un problema si è insinuato nel vostro progetto. Mercurial è dotato di alcune funzionalità ~highly capable~ che vi aiuteranno a isolare le cause dei problemi e a trattarle in maniera appropriata.</para>
Giulio@757 6
Giulio@757 7 <sect1>
Giulio@757 8 <title>Cancellare la cronologia locale</title>
Giulio@757 9
Giulio@757 10 <sect2>
Giulio@757 11 <title>L'inserimento accidentale</title>
Giulio@757 12
Giulio@757 13 <para id="x_d3">Ho l'occasionale ma persistente problema di digitare più velocemente di quanto riesca a pensare, cosa che talvolta ha come risultato io che inserisco un changeset che è incompleto o completamente sbagliato. Nel mio caso, il classico tipo di changeset incompleto è quello in cui ho creato un nuovo file sorgente ma ho dimenticato di usare <command role="hg-cmd">hg add</command> per aggiungerlo al repository. Un changeset <quote>completamente sbagliato</quote> non è così comune, ma non è meno fastidioso.</para>
Giulio@757 14
Giulio@757 15 </sect2>
Giulio@757 16 <sect2 id="sec:undo:rollback">
Giulio@757 17 <title>~Rolling back~ una transazione</title>
Giulio@757 18
Giulio@757 19 <para id="x_d4">Nella <xref linkend="sec:concepts:txn"/>, ho menzionato che Mercurial tratta ogni modifica del repository come una <emphasis>transazione</emphasis>. Ogni volta che inserite un changeset o estraete i cambiamenti da un altro repository, Mercurial ricorda cosa avete fatto. Potete annullare, o <emphasis>~roll back~</emphasis>, esattamente una di queste azioni usando il comando <command role="hg-cmd">hg rollback</command>. (Leggete la <xref linkend="sec:undo:rollback-after-push"/> per un importante avvertimento su come usare questo comando.)</para>
Giulio@757 20
Giulio@757 21 <para id="x_d5">Ecco un errore che mi ritrovo spesso a commettere: inserire un cambiamento in cui ho creato un nuovo file ma ho dimenticato di aggiungerlo tramite <command role="hg-cmd">hg add</command>.</para>
Giulio@757 22
Giulio@757 23 &interaction.rollback.commit;
Giulio@757 24
Giulio@757 25 <para id="x_d6">Controllare il risultato di <command role="hg-cmd">hg status</command> dopo l'inserimento conferma immediatamente l'errore.</para>
Giulio@757 26
Giulio@757 27 &interaction.rollback.status;
Giulio@757 28
Giulio@757 29 <para id="x_d7">Il commit ha catturato le modifiche al file <filename>a</filename>, ma non il nuovo file <filename>b</filename>. Se trasmettessi questo changeset a un repository che condivido con i miei colleghi, è molto probabile che qualcosa in <filename>a</filename> si riferisca a <filename>b</filename>, che non sarà presente nei loro repository quando estrarranno i miei cambiamenti. Di conseguenza, diventerei oggetto di una certa indignazione.</para>
Giulio@757 30
Giulio@757 31 <para id="x_d8">Tuttavia, la fortuna è dalla mia parte&emdash;mi sono accorto dell'errore prima di trasmettere il changeset. Ora uso il comando <command role="hg-cmd">hg rollback</command> e Mercurial farà sparire quell'ultimo changeset.</para>
Giulio@757 32
Giulio@757 33 &interaction.rollback.rollback;
Giulio@757 34
Giulio@757 35 <para id="x_d9">Notate che il changeset non è più presente nella cronologia del repository e che la directory di lavoro pensa ancora che il file <filename>a</filename> sia stato modificato. Il commit e il ~rollback~ hanno lasciato la directory di lavoro nello stato esatto in cui si trovava prima dell'inserimento; il changeset è stato completamente rimosso. Ora posso tranquillamente usare <command role="hg-cmd">hg add</command> per aggiungere il file <filename>b</filename> e rieseguire il commit.</para>
Giulio@757 36
Giulio@757 37 &interaction.rollback.add;
Giulio@757 38
Giulio@757 39 </sect2>
Giulio@757 40 <sect2>
Giulio@757 41 <title>L'estrazione sbagliata</title>
Giulio@757 42
Giulio@757 43 <para id="x_da">&Egrave; pratica comune con Mercurial mantenere i rami di sviluppo separati di un progetto in repository differenti. Il vostro gruppo di sviluppo potrebbe avere un repository condiviso per la release <quote>0.9</quote> del vostro progetto e un altro, contenente cambiamenti differenti, per la release <quote>1.0</quote>.</para>
Giulio@757 44
Giulio@757 45 <para id="x_db">In questa situazione, potete immaginare che pasticcio accadrebbe se aveste un repository <quote>0.9</quote> locale e vi propagaste accidentalmente i cambiamenti dal repository <quote>1.0</quote> condiviso. Nel caso peggiore, potreste non fare abbastanza attenzione e trasmettere quei cambiamenti nell'albero <quote>0.9</quote> condiviso, confondendo tutti gli altri sviluppatori (ma non preoccupatevi, ritorneremo a questo orribile scenario più avanti). Tuttavia, è più probabile che notiate immediatamente l'errore, perché Mercurial vi mostrerà l'URL da cui sta estraendo i cambiamenti, o perché vedrete Mercurial propagare un numero sospettosamente alto di cambiamenti nel repository.</para>
Giulio@757 46
Giulio@759 47 <para id="x_dc">Il comando <command role="hg-cmd">hg rollback</command> cancellerà scrupolosamente tutti i changeset che avete appena estratto. Mercurial raggruppa tutti i cambiamenti provenienti da una invocazione di <command role="hg-cmd">hg pull</command> in una singola transazione, quindi un'unica invocazione di <command role="hg-cmd">hg rollback</command> è tutto quello che vi serve per annullare questo errore.</para>
Giulio@757 48
Giulio@757 49 </sect2>
Giulio@757 50 <sect2 id="sec:undo:rollback-after-push">
Giulio@757 51 <title>~Rolling back~ è inutile se avete già trasmesso le modifiche</title>
Giulio@757 52
Giulio@757 53 <para id="x_dd">Il valore di <command role="hg-cmd">hg rollback</command> scende a zero una volta che avete trasmesso le vostre modifiche a un altro repository. ~Rolling back~ un cambiamento lo fa scomparire interamente, ma <emphasis>solo</emphasis> nel repository in cui invocate <command role="hg-cmd">hg rollback</command>. Dato che un ~rollback~ elimina parte della cronologia, non è possibile che la scomparsa di un cambiamento si propaghi tra i repository.</para>
Giulio@757 54
Giulio@757 55 <para id="x_de">Se avete trasmesso un cambiamento a un altro repository&emdash;in particolare se è un repository condiviso&emdash;è essenzialmente <quote>scappato dal recinto</quote> e dovrete rimediare all'errore in un altro modo. Se trasmettete un changeset da qualche parte, poi ~roll it back~ e poi estraete i cambiamenti dal repository verso cui avete effettuato la trasmissione, il changeset di cui credevate di esservi sbarazzati riapparirà semplicemente nel vostro repository.</para>
Giulio@757 56
Giulio@757 57 <para id="x_df">(Se siete assolutamente sicuri che il cambiamento che volete ritirare è quello più recente contenuto nel repository a cui lo avete trasmesso <emphasis>e</emphasis> sapete che nessun altro può averlo estratto da quel repository, potete ritirare il changeset anche là, ma non dovreste aspettarvi che questo funzioni in maniera affidabile. Presto o tardi un cambiamento finirà in un repository su cui non avete un controllo diretto (o vi siete dimenticati di averlo) e tornerà indietro a mordervi.)</para>
Giulio@757 58
Giulio@757 59 </sect2>
Giulio@757 60 <sect2>
Giulio@757 61 <title>Potete ritirare una sola volta</title>
Giulio@757 62
Giulio@757 63 <para id="x_e0">Mercurial memorizza esattamente una transazione nel suo registro delle transazioni: quella più recente avvenuta nel repository. Questo significa che potete ritirare solo una transazione. Se vi aspettate di poter ritirare una transazione e poi il suo predecessore, questo non il comportamento che otterrete.</para>
Giulio@757 64
Giulio@757 65 &interaction.rollback.twice;
Giulio@757 66
Giulio@757 67 <para id="x_e1">Una volta che avete ritirato una transazione in un repository, non potete effettuare un altro ritiro in quel repository fino a quando non avete eseguito un altro inserimento o una nuova estrazione.</para>
Giulio@757 68
Giulio@757 69 </sect2>
Giulio@757 70 </sect1>
Giulio@757 71 <sect1>
Giulio@757 72 <title>Rimediare alle modifiche sbagliate</title>
Giulio@757 73
Giulio@759 74 <para id="x_e2">Se fate un cambiamento a un file e decidete che in realtà non volevate affatto modificare il file, e non avete ancora inserito i vostri cambiamenti nel repository, il comando che vi serve è <command role="hg-cmd">hg revert</command>. Guarda al changeset che è il genitore della directory di lavoro e ripristina il contenuto di quel file allo stato in cui era in quel changeset. (Questo è un modo prolisso di dire che, nel caso normale, annulla le vostre modifiche.)</para>
Giulio@757 75
Giulio@757 76 <para id="x_e3">Vediamo come funziona il comando <command role="hg-cmd">hg revert</command> attraverso ancora un altro piccolo esempio. Cominceremo modificando un file che Mercurial ha già registrato.</para>
Giulio@757 77
Giulio@757 78 &interaction.daily.revert.modify;
Giulio@757 79
Giulio@757 80 <para id="x_e4">Se non vogliamo quella modifica, possiamo semplicemente usare <command role="hg-cmd">hg revert</command> sul file.</para>
Giulio@757 81
Giulio@757 82 &interaction.daily.revert.unmodify;
Giulio@757 83
Giulio@757 84 <para id="x_e5">Il comando <command role="hg-cmd">hg revert</command> ci fornisce un ulteriore grado di sicurezza salvando il nostro file modificato con una estensione <filename>.orig</filename>.</para>
Giulio@757 85
Giulio@757 86 &interaction.daily.revert.status;
Giulio@757 87
Giulio@757 88 <tip>
Giulio@757 89 <title>Fate attenzione ai file <filename>.orig</filename></title>
Giulio@757 90
Giulio@757 91 <para id="x_6b8">&Egrave; estremamente improbabile che stiate usando Mercurial per gestire file con estensione <filename>.orig</filename> o persino che siate interessati al contenuto di quei file. Nel caso, comunque, è utile ricordare che <command role="hg-cmd">hg revert</command> sovrascriverà incondizionatamente un file con estensione <filename>.orig</filename> esistente. Per esempio, se avete già un file <filename>foo.orig</filename> quando ritornate alla versione precedente del file <filename>foo</filename>, il contenuto di <filename>foo.orig</filename> verrà ~clobbered~.</para>
Giulio@757 92 </tip>
Giulio@757 93
Giulio@757 94 <para id="x_e6">Ecco un riepilogo dei casi che il comando <command role="hg-cmd">hg revert</command> è in grado di gestire. Li descriveremo in dettaglio nella prossima sezione.</para>
Giulio@757 95 <itemizedlist>
Giulio@757 96 <listitem><para id="x_e7">Se modificate un file, lo ripristinerà a suo stato non modificato.</para>
Giulio@757 97 </listitem>
Giulio@757 98 <listitem><para id="x_e8">Se usate <command role="hg-cmd">hg add</command> su un file, annullerà lo stato di <quote>aggiunto</quote> del file ma lascerà intatti i contenuti del file.</para>
Giulio@757 99 </listitem>
Giulio@757 100 <listitem><para id="x_e9">Se cancellate un file senza dirlo a Mercurial, ripristinerà il file con i suoi contenuti intatti.</para>
Giulio@757 101 </listitem>
Giulio@757 102 <listitem><para id="x_ea">Se usate il comando <command role="hg-cmd">hg remove</command> per cancellare un file, annullerà lo stato di <quote>rimosso</quote> del file e ne riprisinterà i contenuti intatti.</para>
Giulio@757 103 </listitem></itemizedlist>
Giulio@757 104
Giulio@757 105 <sect2 id="sec:undo:mgmt">
Giulio@757 106 <title>Errori nella gestione dei file</title>
Giulio@757 107
Giulio@757 108 <para id="x_eb">Il comando <command role="hg-cmd">hg revert</command> è utile non solo per i file modificati. Vi permette di invertire i risultati di tutti i comandi Mercurial di gestione dei file&emdash;<command role="hg-cmd">hg add</command>, <command role="hg-cmd">hg remove</command>, e così via.</para>
Giulio@757 109
Giulio@757 110 <para id="x_ec">Se usate <command role="hg-cmd">hg add</command> su un file, poi decidete che in effetti non volete che Mercurial ne tenga traccia, usate <command role="hg-cmd">hg revert</command> per annullare l'operazione di aggiunta. Non preoccupatevi, Mercurial non modificherà il file in alcun modo, ma si limiterà a eliminare il <quote>contrassegno</quote> per quel file.</para>
Giulio@757 111
Giulio@757 112 &interaction.daily.revert.add;
Giulio@757 113
Giulio@757 114 <para id="x_ed">Similmente, se chiedete a Mercurial di rimuovere un file tramite <command role="hg-cmd">hg remove</command>, potete usare <command role="hg-cmd">hg revert</command> per ripristinarne i contenuti allo stato in cui erano nel genitore della directory di lavoro.</para>
Giulio@757 115
Giulio@757 116 &interaction.daily.revert.remove;
Giulio@757 117
Giulio@757 118 <para id="x_ef">Questo funziona altrettanto bene per un file che avete cancellato a mano senza dirlo a Mercurial (ricordatevi che, nella terminologia di Mercurial, questo file viene detto <quote>mancante</quote>).</para>
Giulio@757 119
Giulio@757 120 &interaction.daily.revert.missing;
Giulio@757 121
Giulio@757 122 <para id="x_ee">Se invertite l'azione del comando <command role="hg-cmd">hg copy</command>, il file copiato rimane nella vostra directory di lavoro senza che Mercurial ne tenga traccia. Dato che l'operazione di copia non ha effetti sul file originale, Mercurial non agisce in alcun modo su quel file.</para>
Giulio@757 123
Giulio@757 124 &interaction.daily.revert.copy;
Giulio@757 125 </sect2>
Giulio@757 126 </sect1>
Giulio@757 127
Giulio@757 128 <sect1>
Giulio@757 129 <title>Gestire i cambiamenti inseriti</title>
Giulio@757 130
Giulio@757 131 <para id="x_f5">Considerate il caso in cui avete inserito un cambiamento <emphasis>a</emphasis> e subito dopo un altro cambiamento <emphasis>b</emphasis> basato sul precedente, poi realizzate che il cambiamento <emphasis>a</emphasis> era sbagliato. Mercurial vi consente di <quote>ritirare</quote> un intero changeset automaticamente e di costruire blocchi che vi permettono di invertire a mano parte di un changeset.</para>
Giulio@757 132
Giulio@757 133 <para id="x_f6">Prima di leggere questa sezione, c'è una cosa che dovete tenere a mente: il comando <command role="hg-cmd">hg backout</command> annulla gli effetti di un cambiamento effettuando una <emphasis>aggiunta</emphasis> alla cronologia del vostro repository, non modificandola o eliminandone una parte. &Egrave; lo strumento giusto da usare se state correggendo un bug, ma non se state cercando di annullare qualche cambiamento che ha conseguenze catastrofiche. Per trattare con questi, leggete la <xref linkend="sec:undo:aaaiiieee"/>.</para>
Giulio@757 134
Giulio@757 135 <sect2>
Giulio@757 136 <title>Ritirare un changeset</title>
Giulio@757 137
Giulio@757 138 <para id="x_f7">Il comando <command role="hg-cmd">hg backout</command> vi consente di <quote>annullare</quote> gli effetti di un intero changeset in modo automatico. Dato che la cronologia di Mercurial è immutabile, questo comando <emphasis>non</emphasis> si sbarazza del changeset che volete annullare, ma crea un nuovo changeset che <emphasis>inverte</emphasis> l'effetto del changeset da annullare.</para>
Giulio@757 139
Giulio@757 140 <para id="x_f8">Le operazioni del comando <command role="hg-cmd">hg backout</command> sono un po' intricate, quindi le illustreremo con alcuni esempi. Per prima cosa, creiamo un repository con alcuni semplici cambiamenti.</para>
Giulio@757 141
Giulio@757 142 &interaction.backout.init;
Giulio@757 143
Giulio@757 144 <para id="x_f9">Il comando <command role="hg-cmd">hg backout</command> prende un singolo identificatore di changeset come argomento; questo è il changeset da annullare. Normalmente, <command role="hg-cmd">hg backout</command> vi presenterà un editor di testo per farvi scrivere un messaggio di commit, in modo che possiate registrare il motivo per cui state ritirando il cambiamento. In questo esempio, forniremo un messaggio di commit sulla riga di comando usando l'opzione <option role="hg-opt-backout">-m</option> option.</para>
Giulio@757 145
Giulio@757 146 </sect2>
Giulio@757 147 <sect2>
Giulio@757 148 <title>Ritirare il changeset di punta </title>
Giulio@757 149
Giulio@757 150 <para id="x_fa">Cominceremo ritirando l'ultimo changeset che abbiamo inserito.</para>
Giulio@757 151
Giulio@757 152 &interaction.backout.simple;
Giulio@757 153
Giulio@757 154 <para id="x_fb">Potete vedere che la seconda riga di <filename>myfile</filename> non è più presente. Un'occhiata all'elenco generato da <command role="hg-cmd">hg log</command> ci dà un'idea di quello che il comando <command role="hg-cmd">hg backout</command> ha fatto.</para>
Giulio@757 155
Giulio@757 156 &interaction.backout.simple.log;
Giulio@757 157
Giulio@757 158 <para id="x_fc">Notate che il nuovo changeset creato da <command role="hg-cmd">hg backout</command> è un figlio del changeset che abbiamo ritirato. Questo è più facile da vedere nella <xref linkend="fig:undo:backout"/>, che mostra una rappresentazione grafica della cronologia dei cambiamenti. Come potete vedere, la cronologia è gradevolmente lineare.</para>
Giulio@757 159
Giulio@757 160 <figure id="fig:undo:backout">
Giulio@757 161 <title>Ritirare un cambiamento tramite il comando <command role="hg-cmd">hg backout</command></title>
Giulio@757 162 <mediaobject>
Giulio@757 163 <imageobject><imagedata fileref="figs/undo-simple.png"/></imageobject>
Giulio@757 164 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 165 </mediaobject>
Giulio@757 166 </figure>
Giulio@757 167
Giulio@757 168 </sect2>
Giulio@757 169 <sect2>
Giulio@757 170 <title>Ritirare un changeset diverso dalla punta</title>
Giulio@757 171
Giulio@757 172 <para id="x_fd">Se volete ritirare un cambiamento diverso dall'ultimo che avete inserito, passate l'opzione <option role="hg-opt-backout">--merge</option> al comando <command role="hg-cmd">hg backout</command>.</para>
Giulio@757 173
Giulio@757 174 &interaction.backout.non-tip.clone;
Giulio@757 175
Giulio@757 176 <para id="x_fe">Questo rende il ritiro di qualsiasi changeset una <quote>singola</quote> operazione che di solito è semplice e veloce.</para>
Giulio@757 177
Giulio@757 178 &interaction.backout.non-tip.backout;
Giulio@757 179
Giulio@757 180 <para id="x_ff">Se date un'occhiata al contenuto di <filename>myfile</filename> dopo che l'operazione di ritiro si è conclusa, vedrete che il primo e il terzo cambiamento sono presenti, ma non il secondo.</para>
Giulio@757 181
Giulio@757 182 &interaction.backout.non-tip.cat;
Giulio@757 183
Giulio@757 184 <para id="x_100">Come illustrato nella rappresentazione grafica della cronologia nella <xref linkend="fig:undo:backout-non-tip"/>, Mercurial inserisce ancora un cambiamento in questo tipo di situazione (il nodo rettangolare è quello che Mercurial inserisce automaticamente) ma il grafo delle revisioni ora è diverso. Prima che Mercurial cominci il processo di ritiro, mantiene in memoria l'identità del genitore corrente della directory di lavoro. Poi ritira il changeset indicato e inserisce quello come un changeset. Infine, incorpora il genitore precedente della directory di lavoro, ma notate che <emphasis>non esegue il commit</emphasis> dei risultati dell'unione. Il repository ora contiene due teste, e la directory di lavoro è in uno ~stato di merge~.</para>
Giulio@757 185
Giulio@757 186 <figure id="fig:undo:backout-non-tip">
Giulio@757 187 <title>Ritiro automatico di un changeset diverso dalla punta tramite il comando <command role="hg-cmd">hg backout</command></title>
Giulio@757 188 <mediaobject>
Giulio@757 189 <imageobject><imagedata fileref="figs/undo-non-tip.png"/></imageobject>
Giulio@757 190 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 191 </mediaobject>
Giulio@757 192 </figure>
Giulio@757 193
Giulio@757 194 <para id="x_103">Il risultato è che siete tornati <quote>indietro a dove eravate</quote>, solo con una parte aggiuntiva di cronologia che annulla gli effetti del changeset che volevate ritirare.</para>
Giulio@757 195
Giulio@757 196 <para id="x_6b9">Potreste chiedervi perché Mercurial non effettua il commit dei risultati dell'unione che ha eseguito. Il motivo è che Mercurial si comporta in maniera conservativa: di solito un'unione ha maggiori probabilità di contenere errori rispetto all'annullamento degli effetti del changeset di punta, quindi il vostro lavoro si troverà più al sicuro se prima ispezionate (e verificherete!) i risultati dell'unione e solo <emphasis>poi</emphasis> li inserite nel repository.</para>
Giulio@757 197
Giulio@757 198 <sect3>
Giulio@757 199 <title>Usate sempre l'opzione <option role="hg-opt-backout">--merge</option></title>
Giulio@757 200
Giulio@757 201 <para id="x_104">In effetti, dato che l'opzione <option role="hg-opt-backout">--merge</option> farà la <quote>cosa giusta</quote> a prescindere dal fatto che il changeset sia quello di punta o meno (i.e. non cercherà di eseguire un'unione se state ritirando la punta, dato che non ce n'è bisogno), dovreste usare <emphasis>sempre</emphasis> questa opzione quando eseguite il comando <command role="hg-cmd">hg backout</command>.</para>
Giulio@757 202
Giulio@757 203 </sect3>
Giulio@757 204 </sect2>
Giulio@757 205 <sect2>
Giulio@757 206 <title>Controllare meglio il processo di ritiro</title>
Giulio@757 207
Giulio@757 208 <para id="x_105">Sebbene vi abbia raccomandato di usare sempre l'opzione <option role="hg-opt-backout">--merge</option> quando ritirate un cambiamento, il comando <command role="hg-cmd">hg backout</command> vi permette di decidere come incorporare un changeset ritirato. Avrete raramente bisogno di controllare il processo di ritiro a mano, ma potrebbe essere utile capire quello che il comando <command role="hg-cmd">hg backout</command> fa per voi automaticamente. Per illustrare questo, cloniamo il nostro primo repository, ma omettiamo il cambiamento ritirato che contiene.</para>
Giulio@757 209
Giulio@757 210 &interaction.backout.manual.clone;
Giulio@757 211
Giulio@757 212 <para id="x_106">Come nel nostro esempio precedente, inseriremo un terzo changeset, poi ritireremo il suo genitore e vedremo cosa succede.</para>
Giulio@757 213
Giulio@757 214 &interaction.backout.manual.backout;
Giulio@757 215
Giulio@757 216 <para id="x_107">Il nostro nuovo changeset è ancora un discendente del changeset che abbiamo ritirato e quindi è una nuova testa, <emphasis>non</emphasis> un discendente di quello che era il changeset di punta. Il comando <command role="hg-cmd">hg backout</command> è stato piuttosto esplicito nel farcelo notare.</para>
Giulio@757 217
Giulio@757 218 &interaction.backout.manual.log;
Giulio@757 219
Giulio@757 220 <para id="x_108">Ancora, è facile vedere quello che è successo guardando al grafo della cronologia delle revisioni, nella <xref linkend="fig:undo:backout-manual"/>. Questo chiarifica che quando usiamo <command role="hg-cmd">hg backout</command> per ritirare un cambiamento diverso dalla punta, Mercurial aggiunge una nuova testa al repository (il cambiamento che ha inserito ha la forma di un rettangolo).</para>
Giulio@757 221
Giulio@757 222 <figure id="fig:undo:backout-manual">
Giulio@757 223 <title>Ritirare un cambiamento tramite il comando <command role="hg-cmd">hg backout</command></title>
Giulio@757 224 <mediaobject>
Giulio@757 225 <imageobject><imagedata fileref="figs/undo-manual.png"/></imageobject>
Giulio@757 226 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 227 </mediaobject>
Giulio@757 228 </figure>
Giulio@757 229
Giulio@757 230 <para id="x_10a">Dopo che il comando <command role="hg-cmd">hg backout</command> ha terminato, lascia il nuovo changeset <quote>ritirato</quote> come il genitore della directory di lavoro.</para>
Giulio@757 231
Giulio@757 232 &interaction.backout.manual.parents;
Giulio@757 233
Giulio@757 234 <para id="x_10b">Ora abbiamo due insiemi isolati di cambiamenti.</para>
Giulio@757 235
Giulio@757 236 &interaction.backout.manual.heads;
Giulio@757 237
Giulio@757 238 <para id="x_10c">Pensiamo a quello che ora ci aspettiamo di vedere come contenuto di <filename>myfile</filename>. Il primo cambiamento dovrebbe essere presente, perché non lo abbiamo mai ritirato. Il secondo cambiamento non dovrebbe esserci, dato che quello è il cambiamento che abbiamo ritirato. Visto che il grafo della cronologia mostra il terzo cambiamento come una testa separata, <emphasis>non</emphasis> ci aspettiamo di vedere il terzo cambiamento nel contenuto di <filename>myfile</filename>.</para>
Giulio@757 239
Giulio@757 240 &interaction.backout.manual.cat;
Giulio@757 241
Giulio@757 242 <para id="x_10d">Per riottenere il terzo cambiamento nel file, eseguiamo semplicemente una normale unione tra le nostre due teste.</para>
Giulio@757 243
Giulio@757 244 &interaction.backout.manual.merge;
Giulio@757 245
Giulio@757 246 <para id="x_10e">Successivamente, la cronologia del nostro repository può essere rappresentata graficamente come nella <xref linkend="fig:undo:backout-manual-merge"/>.</para>
Giulio@757 247
Giulio@757 248 <figure id="fig:undo:backout-manual-merge">
Giulio@757 249 <title>Incorporare manualmente un cambiamento ritirato</title>
Giulio@757 250 <mediaobject>
Giulio@757 251 <imageobject><imagedata fileref="figs/undo-manual-merge.png"/></imageobject>
Giulio@757 252 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 253 </mediaobject>
Giulio@757 254 </figure>
Giulio@757 255
Giulio@757 256 </sect2>
Giulio@757 257 <sect2>
Giulio@757 258 <title>Perché <command role="hg-cmd">hg backout</command> funziona in questo modo</title>
Giulio@757 259
Giulio@757 260 <para id="x_110">Ecco una breve descrizione del funzionamento del comando <command role="hg-cmd">hg backout</command>.</para>
Giulio@757 261 <orderedlist>
Giulio@757 262 <listitem><para id="x_111">Si assicura che la directory di lavoro sia <quote>pulita</quote>, i.e. che l'elenco generato da <command role="hg-cmd">hg status</command> sia vuoto.</para>
Giulio@757 263 </listitem>
Giulio@757 264 <listitem><para id="x_112">Memorizza il genitore corrente della directory di lavoro. Chiamiamo <literal>orig</literal> questo changeset.</para>
Giulio@757 265 </listitem>
Giulio@757 266 <listitem><para id="x_113">Esegue l'equivalente di una invocazione di <command role="hg-cmd">hg update</command> per sincronizzare la directory di lavoro con il changeset che volete ritirare. Chiamiamo <literal>backout</literal> questo changeset.</para>
Giulio@757 267 </listitem>
Giulio@757 268 <listitem><para id="x_114">Trova il genitore di quel changeset. Chiamiamo <literal>parent</literal> questo changeset.</para>
Giulio@757 269 </listitem>
Giulio@757 270 <listitem><para id="x_115">Per ogni file su cui il changeset <literal>backout</literal> ha avuto effetto, esegue l'equivalente del comando <command role="hg-cmd">hg revert -r parent</command> su quel file per ripristinare il contenuto che aveva prima che quel changeset venisse inserito.</para>
Giulio@757 271 </listitem>
Giulio@757 272 <listitem><para id="x_116">Esegue il commit del risultato come un nuovo changeset che ha <literal>backout</literal> come genitore.</para>
Giulio@757 273 </listitem>
Giulio@757 274 <listitem><para id="x_117">Se specificate l'opzione <option role="hg-opt-backout">--merge</option> sulla riga di comando, esegue un'unione con <literal>orig</literal> e inserisce i risultati dell'unione nel repository.</para>
Giulio@757 275 </listitem></orderedlist>
Giulio@757 276
Giulio@757 277 <para id="x_118">In alternativa, sarebbe possibile implementare <command role="hg-cmd">hg backout</command> utilizzando <command role="hg-cmd">hg export</command> per esportare il changeset da ritirare sotto forma di diff e poi impiegando l'opzione <option role="cmd-opt-patch">--reverse</option> del comando <command>patch</command> per invertire l'effetto del cambiamento senza gingillarsi con la directory di lavoro. Questo sembra molto più semplice, ma non funzionerebbe affatto altrettanto bene.</para>
Giulio@757 278
Giulio@757 279 <para id="x_119">Il comando <command role="hg-cmd">hg backout</command> esegue un aggiornamento, un inserimento, un'unione e un altro inserimento per dare al meccanismo di unione la possibilità di fare il miglior lavoro possibile nel gestire tutte le modifiche avvenute <emphasis>tra</emphasis> il cambiamento che state ritirando e la revisione di punta corrente.</para>
Giulio@757 280
Giulio@757 281 <para id="x_11a">Se state ritirando un cambiamento che si trova 100 revisioni indietro nella cronologia del vostro progetto, le probabilità che il comando <command>patch</command> sia in grado di applicare un diff invertito in maniera pulita non sono molto alte, perché i cambiamenti intercorsi avranno probabilmente <quote>rotto il contesto</quote> utilizzato da <command>patch</command> per determinare se può applicare una patch (se questo vi sembra incomprensibile, leggete <!--<xref linkend="sec:mq:patch"/>-->FIXME per una discussione sul comando <command>patch</command>). In più, il meccanismo di unione di Mercurial riesce gestire i cambiamenti di nome e permessi per file e directory e le modifiche ai file binari, mentre <command>patch</command> non è in grado di farlo.</para>
Giulio@757 282
Giulio@757 283 </sect2>
Giulio@757 284 </sect1>
Giulio@757 285 <sect1 id="sec:undo:aaaiiieee">
Giulio@757 286 <title>Modifiche che non avrebbero mai dovuto essere fatte</title>
Giulio@757 287
Giulio@757 288 <para id="x_11b">Quasi sempre, il comando <command role="hg-cmd">hg backout</command> è esattamente quello che vi serve se volete annullare gli effetti di un cambiamento. Lascia una registrazione permanente di quello che avete fatto, sia quando avete inserito il changeset originale che quando avete successivamente rimesso in ordine.</para>
Giulio@757 289
Giulio@757 290 <para id="x_11c">In rare occasioni, comunque, potreste scoprire di aver inserito un cambiamento che non dovrebbe essere presente nel repository proprio per niente. Per esempio, sarebbe molto inusuale, e di solito considerato un errore, inserire in un repository i file oggetto di un progetto software insieme ai suoi file sorgente. I file oggetto non hanno praticamente alcun valore intrinserco e sono <emphasis>grandi</emphasis>, quindi aumentano la dimensione del repository e il tempo necessario a clonarlo o a estrarne i cambiamenti.</para>
Giulio@757 291
Giulio@757 292 <para id="x_11d">Prima di illustrare le opzioni che avete se eseguite il commit di un cambiamento <quote>da sacchetto di carta marrone</quote> (quel tipo di modifiche così cattive che vorreste nascondere la testa in un sacchetto di carta marrone), lasciatemi discutere alcuni approcci che probabilmente non funzioneranno.</para>
Giulio@757 293
Giulio@757 294 <para id="x_11e">Dato che Mercurial tratta la cronologia in maniera cumulativa&emdash;ogni cambiamento si basa su tutti i cambiamenti che lo precedono&emdash;in genere non potete far semplicemente sparire i cambiamenti disastrosi. L'unica eccezione è quando avete appena inserito una modifica e non è stata ancora propagata verso qualche altro repository. In questo caso, potete tranquillamente usare il comando <command role="hg-cmd">hg rollback</command>, come descritto nella <xref linkend="sec:undo:rollback"/>.</para>
Giulio@757 295
Giulio@757 296 <para id="x_11f">Dopo che avete trasmesso un cambiamento sbagliato a un altro repository, <emphasis>potreste</emphasis> anocra usare <command role="hg-cmd">hg rollback</command> per far scomparire la vostra copia locale del cambiamento, ma questa azione non avrà le conseguenze che volete. Il cambiamento sarà ancora presente nel repository remoto, quindi riapparirà nel vostro repository locale la prossima volta che ne estrarrete i cambiamenti.</para>
Giulio@757 297
Giulio@757 298 <para id="x_120">Se vi trovate in una situazione come questa e sapete verso quali repository si è propagato il vostro cambiamento sbagliato, potete <emphasis>provare</emphasis> a sbarazzarvi del cambiamento in <emphasis>ognuno</emphasis> di quei repository. Questa, naturalmente, non è una soluzione soddisfacente: se mancate anche un singolo repository quando state ripulendo, il cambiamento sarà ancora <quote>là fuori</quote> e potrebbe propagarsi ulteriormente.</para>
Giulio@757 299
Giulio@757 300 <para id="x_121">Se avete inserito uno o più cambiamenti <emphasis>dopo</emphasis> il cambiamento che vorreste veder sparire, le vostre opzioni si riducono ulteriormente. Mercurial non offre alcun modo per <quote>fare un buco</quote> nella cronologia lasciando gli altri changeset intatti.</para>
Giulio@757 301
Giulio@757 302 <sect2>
Giulio@757 303 <title>Ritirare un'unione</title>
Giulio@757 304
Giulio@757 305 <para id="x_6ba">Dato che le unioni sono spesso complicate, si sono sentiti casi di unioni gravemente rovinate, ma i cui risultati sono stati erroneamente inseriti in un repository. Mercurial fornisce un'importante protezione contro le unioni sbagliate rifiutandosi di eseguire il commit di file non risolti, ma l'ingenuità umana garantisce che è ancora possibile mettere sottosopra un'unione e inserirla.</para>
Giulio@757 306
Giulio@757 307 <para id="x_6bb">Data un'unione sbagliata che è stata inserita, di solito il miglior modo di affrontarla è semplicemente provare a riparare il danno a mano. Un completo disastro che non può essere corretto a mano dovrebbe essere molto raro, ma il comando <command role="hg-cmd">hg backout</command> può aiutare a rendere la pulizia più semplice. Offre un'opzione <option role="hg-opt-backout">--parent</option>, che vi consente di specificare a quale genitore tornare quando state ritirando un'unione.</para>
Giulio@757 308
Giulio@757 309 <figure id="fig:undo:bad-merge-1">
Giulio@757 310 <title>Un'unione sbagliata</title>
Giulio@757 311 <mediaobject>
Giulio@757 312 <imageobject><imagedata fileref="figs/bad-merge-1.png"/></imageobject>
Giulio@757 313 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 314 </mediaobject>
Giulio@757 315 </figure>
Giulio@757 316
Giulio@757 317 <para id="x_6bc">Supponete di avere un grafo delle revisioni simile a quello della <xref linkend="fig:undo:bad-merge-1"/>. Quello che vorremmo fare è <emphasis>rifare</emphasis> l'unione tra le revisioni 2 e 3.</para>
Giulio@757 318
Giulio@757 319 <para id="x_6bd">Potremmo eseguire questa operazione nel modo seguente.</para>
Giulio@757 320
Giulio@757 321 <orderedlist>
Giulio@757 322 <listitem>
Giulio@757 323 <para id="x_6be">Invocare <command role="hg-cmd">hg backout --rev=4 --parent=2</command>. Questo dice al comando <command role="hg-cmd">hg backout</command> di ritirare la revisione 4, che è l'unione sbagliata, e di scegliere il genitore 2, uno dei genitori dell'unione, nel momento di decidere quale revisione preferire. L'effetto del comando può essere visto nella <xref linkend="fig:undo:bad-merge-2"/>.</para>
Giulio@757 324 <figure id="fig:undo:bad-merge-2">
Giulio@757 325 <title>Ritirare l'unione favorendo un genitore</title>
Giulio@757 326 <mediaobject>
Giulio@757 327 <imageobject><imagedata fileref="figs/bad-merge-2.png"/></imageobject>
Giulio@757 328 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 329 </mediaobject>
Giulio@757 330 </figure>
Giulio@757 331 </listitem>
Giulio@757 332
Giulio@757 333 <listitem>
Giulio@757 334 <para id="x_6bf">Invocare <command role="hg-cmd">hg backout --rev=4 --parent=3</command>. Questo dice al comando <command role="hg-cmd">hg backout</command> di ritirare ancora la revisione 4, ma questa volta scegliendo il genitore 3, l'altro genitore dell'unione. Il risultato è visibile nella <xref linkend="fig:undo:bad-merge-3"/>, in cui il repository ora contiene tre teste.</para>
Giulio@757 335 <figure id="fig:undo:bad-merge-3">
Giulio@757 336 <title>Ritirare l'unione favorendo l'altro genitore</title>
Giulio@757 337 <mediaobject>
Giulio@757 338 <imageobject><imagedata fileref="figs/bad-merge-3.png"/></imageobject>
Giulio@757 339 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 340 </mediaobject>
Giulio@757 341 </figure>
Giulio@757 342 </listitem>
Giulio@757 343
Giulio@757 344 <listitem>
Giulio@757 345 <para id="x_6c0">Rifare l'unione sbagliata unendo le due teste generate dai ritiri, riducendo quindi a due il numero di teste nel repository, come si può vedere nella <xref linkend="fig:undo:bad-merge-4"/>.</para>
Giulio@757 346 <figure id="fig:undo:bad-merge-4">
Giulio@757 347 <title>Unire i risultati dei ritiri</title>
Giulio@757 348 <mediaobject>
Giulio@757 349 <imageobject><imagedata fileref="figs/bad-merge-4.png"/></imageobject>
Giulio@757 350 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 351 </mediaobject>
Giulio@757 352 </figure>
Giulio@757 353 </listitem>
Giulio@757 354
Giulio@757 355 <listitem>
Giulio@757 356 <para id="x_6c1">Eseguire un'unione con il commit che è stato eseguito dopo l'unione sbagliata, come mostrato nella <xref linkend="fig:undo:bad-merge-5"/>.</para>
Giulio@757 357 <figure id="fig:undo:bad-merge-5">
Giulio@757 358 <title>Unire i risultati dei ritiri</title>
Giulio@757 359 <mediaobject>
Giulio@757 360 <imageobject><imagedata fileref="figs/bad-merge-5.png"/></imageobject>
Giulio@757 361 <textobject><phrase>XXX add text</phrase></textobject>
Giulio@757 362 </mediaobject>
Giulio@757 363 </figure>
Giulio@757 364 </listitem>
Giulio@757 365 </orderedlist>
Giulio@757 366 </sect2>
Giulio@757 367
Giulio@757 368 <sect2>
Giulio@757 369 <title>Proteggervi dai cambiamenti che vi sono <quote>sfuggiti</quote></title>
Giulio@757 370
Giulio@757 371 <para id="x_123">Se avete inserito alcuni cambiamenti nel vostro repositor locale e li avete propagati da qualche altra parte, questo non costituisce necessariamente un disastro. Potete proteggervi prevenendo la comparsa di alcuni tipi di changeset sbagliati. Questo è particolarmente facile se il vostro gruppo di lavoro di solito estrae i cambiamenti da un repository centrale.</para>
Giulio@757 372
Giulio@758 373 <para id="x_124">Configurando alcuni hook su quel repository per validare i changeset in entrata (si veda il <!--<xref linkend="chap:hook"/>-->FIXME), potete automaticamente evitare che alcuni tipi di changeset sbagliati compaiano nel repository centrale. Con una tale configurazione, alcuni tipi di changeset sbagliati tenderanno naturalmente a <quote>estinguersi</quote> perché non possono propagarsi verso il repository centrale. Ancora meglio, questo accade senza alcun bisogno di un intervento esplicito.</para>
Giulio@757 374
Giulio@757 375 <para id="x_125">Per esempio, un hook sui cambiamenti in entrata che verifica che un changeset si riesca effettivamente a compilare può prevenire involontari <quote>guasti</quote> al processo di assemblaggio.</para>
Giulio@757 376 </sect2>
Giulio@757 377
Giulio@757 378 <sect2>
Giulio@757 379 <title>Cosa fare con i cambiamenti sensibili che sfuggono</title>
Giulio@757 380
Giulio@757 381 <para id="x_6c2">Persino un progetto gestito con attenzione può subire uno sfortunato evento come il commit e l'incontrollata propagazione di un file che contiene password importanti.</para>
Giulio@757 382
Giulio@757 383 <para id="x_6c3">Se qualcosa del genere dovesse accadervi e le informazioni che vengono accidentalmente propagate sono davvero sensibili, il vostro primo passo dovrebbe essere quello di mitigare l'effetto della perdita senza cercare di controllare la perdita stessa. Se non siete sicuri al 100% di sapere esattamente chi può aver visto i cambiamenti, dovreste immediatamente cambiare le password, cancellare le carte di credito, o trovare qualche altro modo per assicurarvi che le informazioni fuoriuscite non siano più utili. In altre parole, assumete che il cambiamento si sia propagato in lungo e in largo e che non ci sia più niente che potete fare.</para>
Giulio@757 384
Giulio@757 385 <para id="x_6c4">Potreste sperare che ci sia qualche meccanismo che potete usare per scoprire chi ha visto un cambiamento o per cancellare il cambiamento permanentemente e ovunque, ma ci sono buone ragioni per cui queste operazioni non sono possibili.</para>
Giulio@757 386
Giulio@757 387 <para id="x_6c5">Mercurial non fornisce una ~audit trail~ di chi ha estratto i cambiamenti da un repository, perché di solito questa informazione è impossibile registrare o è facile da imbrogliare. In un ambiente multi-utente o di rete, dovreste quindi dubitare estremamente di voi stessi se pensate di aver identificato ogni luogo in cui un cambiamento sensibile si è propagato. Non dimenticate che le persone possono spedire pacchetti via email, salvare i propri dati su altri computer tramite il software di backup, trasportare i repository su chiavi USB e trovare altri modi completamente innocenti di confondere i vostri tentativi di ritrovare ogni copia di un cambiamento problematico.</para>
Giulio@757 388
Giulio@757 389 <para id="x_6c6">In più, Mercurial non vi fornisce un modo per far completamente sparire un changeset dalla cronologia perché non c'è alcun modo di imporre la sua sparizione, dato che qualcuno potrebbe facilmente modificare la propria copia di Mercurial per ignorare quelle direttive. E poi, se anche Mercurial fornisse questa funzionalità, qualcuno che semplicemente non abbia estratto il changeset che <quote>fa sparire questo file</quote> non ne godrebbe gli effetti, né lo farebbero i ~web crawler~ che visitano un repository al momento sbagliato, i backup del disco, o altri meccanismi. In effetti, nessun sistema distribuito di controllo di revisione può far sparire dati in maniera affidabile. Dare l'illusione di un controllo di questo tipo potrebbe facilmente fornirvi un falso senso di sicurezza, peggiorando le cose rispetto a non darvela affatto.</para>
Giulio@757 390 </sect2>
Giulio@757 391 </sect1>
Giulio@757 392
Giulio@757 393 <sect1 id="sec:undo:bisect">
Giulio@757 394 <title>Trovare la causa di un bug</title>
Giulio@757 395
Giulio@757 396 <para id="x_126">Mentre va benissimo essere in grado di ritirare un changeset che ha introdotto un bug, questo richiede che sappiate quale changeset va ritirato. Mercurial offre un inestimabile comando, chiamato <command role="hg-cmd">hg bisect</command>, che vi aiuta ad automatizzare questo processo e a completarlo in maniera molto efficiente.</para>
Giulio@757 397
Giulio@757 398 <para id="x_127">L'idea dietro al comando <command role="hg-cmd">hg bisect</command> è che un changeset ha introdotto una modifica di comportamento che potete identificare con un semplice test binario di successo o fallimento. Non sapete quale porzione di codice ha introdotto il cambiamento, ma sapete come verificare la presenza del bug. Il comando <command role="hg-cmd">hg bisect</command> usa il vostro test per dirigere la propria ricerca del changeset che ha introdotto il codice che ha causato il bug.</para>
Giulio@757 399
Giulio@757 400 <para id="x_128">Ecco alcuni scenari per aiutarvi a capire come potreste applicare questo comando.</para>
Giulio@757 401 <itemizedlist>
Giulio@757 402 <listitem><para id="x_129">La versione più recente del vostro software ha un bug che non ricordate fosse presente alcune settimane prima, ma non sapete quando il bug è stato introdotto. Qui, i vostri test binari controllano la presenza di quel bug.</para>
Giulio@757 403 </listitem>
Giulio@757 404 <listitem><para id="x_12a">Avete corretto un bug in tutta fretta e ora è il momento di chiudere la relativa voce nel database dei bug del vostro gruppo. Il database dei bug richiede un identificatore di changeset quando chiudete una voce, ma non ricordate in quale changeset avete corretto il bug. Ancora una volta, il vostro test binario controlla la presenza del bug.</para>
Giulio@757 405 </listitem>
Giulio@757 406 <listitem><para id="x_12b">Il vostro software funziona correttamente, ma più lento del 15% rispetto all'ultima volta che avete compiuto questa misurazione. Volete sapere quale changeset ha introdotto la perdita di prestazioni. In questo caso, il vostro test binario misura le prestazioni del vostro software per vedere se è <quote>veloce</quote> o <quote>lento</quote>.</para>
Giulio@757 407 </listitem>
Giulio@757 408 <listitem><para id="x_12c">La dimensione dei componenti del progetto che rilasciate è esplosa di recente e sospettate che qualcosa sia cambiato nel modo in cui assemblate il progetto.</para>
Giulio@757 409 </listitem></itemizedlist>
Giulio@757 410
Giulio@757 411 <para id="x_12d">Da questi esempi, dovrebbe essere chiaro che il comando <command role="hg-cmd">hg bisect</command> non è utile solo per trovare le cause dei bug. Potete usarlo per trovare qualsiasi <quote>proprietà emergente</quote> di un repository (qualsiasi cosa che non potete trovare con una semplice ricerca di testo sui file contenuti nell'albero) per la quale sia possibile scrivere un test binario.</para>
Giulio@757 412
Giulio@757 413 <para id="x_12e">Ora introdurremo un po' di terminologia, giusto per chiarire quali sono le parti del processo di ricerca di cui siete responsabili e quali sono quelle di cui è responsabile Mercurial. Un <emphasis>test</emphasis> è qualcosa che <emphasis>voi</emphasis> eseguite quando <command role="hg-cmd">hg bisect</command> sceglie un changeset. Una <emphasis>sonda</emphasis> è ciò che <command role="hg-cmd">hg bisect</command> esegue per dirvi se una revisione è buona. Infine, useremo la parola <quote>bisezione</quote> come nome e <quote>bisezionare</quote> come verbo per intendere la <quote>ricerca tramite il comando <command role="hg-cmd">hg bisect</command></quote>.</para>
Giulio@757 414
Giulio@757 415 <para id="x_12f">Un modo semplice per automatizzare il processo di ricerca sarebbe quello di collaudare semplicemente ogni changeset. Tuttavia, questo scala malamente. Se ci volessero dieci minuti per collaudare un singolo changeset e aveste 10.000 changeset nel vostro repository, l'approccio completo impiegherebbe una media di 35 <emphasis>giorni</emphasis> per trovare il changeset che ha introdotto un bug. Anche se sapeste che il bug è stato introdotto in uno degli ultimi 500 changeset e limitaste la ricerca a quelli, dovrebbero passare più di 40 ore di attesa per trovare il changeset che ha introdotto il vostro bug.</para>
Giulio@757 416
Giulio@757 417 <para id="x_130">Il comando <command role="hg-cmd">hg bisect</command> invece usa la propria conoscenza della <quote>forma</quote> della cronologia delle revisioni del vostro progetto per effettuare una ricerca in tempo proporzionale al <emphasis>logaritmo</emphasis> del numero dei changeset da controllare (il tipo di ricerca che esegue viene chiamata ricerca dicotomica). Con questo approccio, la ricerca attraverso 10.000 changeset impiegherà meno di 3 ore, anche a 10 minuti per ogni test (la ricerca richiederà circa 14 test). Limitate la vostra ricerca agli ultimi cento changeset e il tempo impiegato sarà solo circa un'ora (approssimativamente sette test).</para>
Giulio@757 418
Giulio@757 419 <para id="x_131">Il comando <command role="hg-cmd">hg bisect</command> è consapevole della natura <quote>ramificata</quote> della cronologia delle revisioni di un progetto Mercurial, quindi non ha problemi a trattare con rami, unioni, o molteplici teste in un repository. Opera in maniera così efficiente perché è in grado di potare interi rami di cronologia con una singola sonda.</para>
Giulio@757 420
Giulio@757 421 <sect2>
Giulio@757 422 <title>Usare il comando <command role="hg-cmd">hg bisect</command></title>
Giulio@757 423
Giulio@757 424 <para id="x_132">Ecco un esempio di <command role="hg-cmd">hg bisect</command> in azione.</para>
Giulio@757 425
Giulio@757 426 <note>
Giulio@757 427 <para id="x_133">Fino alla versione 0.9.5 di Mercurial compresa, <command role="hg-cmd">hg bisect</command> non era uno dei comandi principali, ma veniva distribuito con Mercurial sotto forma di estensione. Questa sezione descrive il comando predefinito, non la vecchia estensione.</para>
Giulio@757 428 </note>
Giulio@757 429
Giulio@757 430 <para id="x_134">Ora creiamo un nuovo repository in modo che possiate provare il comando <command role="hg-cmd">hg bisect</command> in isolamento.</para>
Giulio@757 431
Giulio@757 432 &interaction.bisect.init;
Giulio@757 433
Giulio@757 434 <para id="x_135">Simuleremo un progetto che ha un bug in modo molto semplice: creiamo cambiamenti elementari in un ciclo e nomineremo uno specifico cambiamento che conterrà il <quote>bug</quote>. Questo ciclo crea 35 changeset, ognuno dei quali aggiunge un singolo file al repository. Rappresenteremo il nostro <quote>bug</quote> con un file che contiene il testo <quote>i have a gub</quote>.</para>
Giulio@757 435
Giulio@757 436 &interaction.bisect.commits;
Giulio@757 437
Giulio@757 438 <para id="x_136">La prossima cosa che vorremmo fare è capire come usare il comando <command role="hg-cmd">hg bisect</command>. Possiamo usare il normale meccanismo di aiuto predefinito di Mercurial per fare questo.</para>
Giulio@757 439
Giulio@757 440 &interaction.bisect.help;
Giulio@757 441
Giulio@757 442 <para id="x_137">Il comando <command role="hg-cmd">hg bisect</command> lavora in più passi. Ogni passo procede nella maniera seguente.</para>
Giulio@757 443 <orderedlist>
Giulio@757 444 <listitem><para id="x_138">Eseguite il vostro test binario.</para>
Giulio@757 445 <itemizedlist>
Giulio@757 446 <listitem><para id="x_139">Se il test ha avuto successo, informate <command role="hg-cmd">hg bisect</command> invocando il comando <command role="hg-cmd">hg bisect --good</command>.</para>
Giulio@757 447 </listitem>
Giulio@757 448 <listitem><para id="x_13a">Se il test è fallito, invocate il comando <command role="hg-cmd">hg bisect --bad</command>.</para>
Giulio@758 449 </listitem>
Giulio@758 450 </itemizedlist>
Giulio@757 451 </listitem>
Giulio@757 452 <listitem><para id="x_13b">Il comando usa le vostre informazioni per decidere quale changeset collaudare successivamente.</para>
Giulio@757 453 </listitem>
Giulio@757 454 <listitem><para id="x_13c">Il comando aggiorna la directory di lavoro a quel changeset e il processo ricomincia da capo.</para>
Giulio@757 455 </listitem></orderedlist>
Giulio@757 456 <para id="x_13d">Il processo termina quando <command role="hg-cmd">hg bisect</command> identifica un unico cambiamento che contrassegna il punto in cui il vostro test passa dallo stato di <quote>successo</quote> a quello di <quote>fallimento</quote>.</para>
Giulio@757 457
Giulio@757 458 <para id="x_13e">Per cominciare la ricerca, dobbiamo eseguire il comando <command role="hg-cmd">hg bisect --reset</command>.</para>
Giulio@757 459
Giulio@757 460 &interaction.bisect.search.init;
Giulio@757 461
Giulio@757 462 <para id="x_13f">Nel nostro caso, il test binario che usiamo è semplice: controlliamo per vedere se qualche file nel repository contiene la stringa <quote>i have a gub</quote>. Se è così, questo changeset contiene il cambiamento che ha <quote>causato il bug</quote>. Per convenzione, un changeset che ha la proprietà che stiamo cercando è <quote>guasto</quote>, mentre uno che non ce l'ha è <quote>corretto</quote>.</para>
Giulio@757 463
Giulio@757 464 <para id="x_140">Quasi sempre, la revisione su cui la directory di lavoro è sincronizzata (di solito, la punta) esibisce già il problema introdotto dal cambiamento malfunzionante, quindi la contrassegneremo come <quote>guasta</quote>.</para>
Giulio@757 465
Giulio@757 466 &interaction.bisect.search.bad-init;
Giulio@757 467
Giulio@757 468 <para id="x_141">Il nostro compito successivo consiste nel nominare un changeset che sappiamo <emphasis>non</emphasis> contenere il bug, in modo che il comando <command role="hg-cmd">hg bisect</command> possa <quote>circoscrivere</quote> la ricerca tra il primo changeset corretto e il primo changeset guasto. Nel nostro caso, sappiamo che la revisione 10 non conteneva il bug. (Spiegherò meglio come scegliere il primo changeset <quote>corretto</quote> più avanti.)</para>
Giulio@757 469
Giulio@757 470 &interaction.bisect.search.good-init;
Giulio@757 471
Giulio@757 472 <para id="x_142">Notate che questo comando ha stampato alcune informazioni.</para>
Giulio@757 473 <itemizedlist>
Giulio@757 474 <listitem><para id="x_143">Ci ha detto quanti changeset deve considerare prima di poter identificare quello che ha introdotto il bug e quanti test saranno richiesti dal processo.</para>
Giulio@757 475 </listitem>
Giulio@757 476 <listitem><para id="x_144">Ha aggiornato la directory di lavoro al prossimo changeset da collaudare e ci ha detto quale changeset sta collaudando.</para>
Giulio@757 477 </listitem></itemizedlist>
Giulio@757 478
Giulio@757 479 <para id="x_145">Ora eseguiamo il nostro test nella directory di lavoro, usando il comando <command>grep</command> per vedere se il nostro file <quote>guasto</quote> è presente. Se c'è, questa revisione è guasta, altrimenti è corretta.</para>
Giulio@757 480
Giulio@757 481 &interaction.bisect.search.step1;
Giulio@757 482
Giulio@757 483 <para id="x_146">Questo test sembra un perfetto candidato per l'automazione, quindi trasformiamolo in una funzione di shell.</para>
Giulio@757 484
Giulio@757 485 &interaction.bisect.search.mytest;
Giulio@757 486
Giulio@757 487 <para id="x_147">Ora possiamo eseguire un intero passo di collaudo con il singolo comando <literal>mytest</literal>.</para>
Giulio@757 488
Giulio@757 489 &interaction.bisect.search.step2;
Giulio@757 490
Giulio@757 491 <para id="x_148">Ancora qualche altra invocazione del comando che abbiamo preparato per il passo di collaudo e abbiamo finito.</para>
Giulio@757 492
Giulio@757 493 &interaction.bisect.search.rest;
Giulio@757 494
Giulio@757 495 <para id="x_149">Anche se avevamo 40 changeset attraverso cui cercare, il comando <command role="hg-cmd">hg bisect</command> ci ha permesso di trovare il changeset che ha introdotto il nostro <quote>bug</quote> usando solo cinque test. Dato che il numero di test effettuati dal comando <command role="hg-cmd">hg bisect</command> cresce con il logaritmo del numero dei changeset da analizzare, il vantaggio che ha rispetto a una ricerca che usa la strategia della <quote>forza bruta</quote> aumenta con ogni changeset che aggiungete.</para>
Giulio@757 496
Giulio@757 497 </sect2>
Giulio@757 498 <sect2>
Giulio@757 499 <title>Riordinare dopo la vostra ricerca</title>
Giulio@757 500
Giulio@757 501 <para id="x_14a">Quando avete finito di usare il comando <command role="hg-cmd">hg bisect</command> in un repository, potete invocare il comando <command role="hg-cmd">hg bisect --reset</command> per scartare le informazioni che venivano usate per guidare la vostra ricerca. Il comando non usa molto spazio, quindi non importa se vi dimenticate di effettuare questa esecuzione. Tuttavia, <command role="hg-cmd">hg bisect</command> non vi permetterà di cominciare una nuova ricerca in quel repository fino a quando non avrete eseguito <command role="hg-cmd">hg bisect --reset</command>.</para>
Giulio@757 502
Giulio@757 503 &interaction.bisect.search.reset;
Giulio@757 504
Giulio@757 505 </sect2>
Giulio@757 506 </sect1>
Giulio@757 507 <sect1>
Giulio@757 508 <title>Suggerimenti per trovare efficacemente i bug</title>
Giulio@757 509
Giulio@757 510 <sect2>
Giulio@757 511 <title>Fornire informazioni consistenti</title>
Giulio@757 512
Giulio@757 513 <para id="x_14b">Il comando <command role="hg-cmd">hg bisect</command> vi richiede di riportare correttamente il risultato di ogni test che eseguite. Se dite al comando che un test è fallito quando in realtà ha avuto successo, <emphasis>potrebbe</emphasis> essere in grado di scoprire l'inconsistenza. Se può identificare un'incosistenza nei vostri resoconti, vi dirà che un particolare changeset è sia corretto che guasto. Tuttavia, non è in grado di farlo perfettamente ed è ugualmente probabile che vi restituisca il changeset sbagliato come causa del bug.</para>
Giulio@757 514
Giulio@757 515 </sect2>
Giulio@757 516 <sect2>
Giulio@757 517 <title>Automatizzare il più possibile</title>
Giulio@757 518
Giulio@757 519 <para id="x_14c">Quando ho cominciato a usare il comando <command role="hg-cmd">hg bisect</command>, ho provato a eseguire alcune volte i miei test a mano sulla riga di comando. Questo è un approccio che almeno a me non si addice. Dopo alcune prove, ho visto che stavo facendo abbastanza errori da dover ricominciare le mie ricerche diverse volte prima di riuscire a ottenere i risultati corretti.</para>
Giulio@757 520
Giulio@757 521 <para id="x_14d">I miei problemi iniziali nel guidare a mano il comando <command role="hg-cmd">hg bisect</command> si sono verificati anche con ricerche semplici su repository di piccole dimensioni, ma se il problema che state cercando è più sottile, o se il numero di test che <command role="hg-cmd">hg bisect</command> deve eseguire aumenta, la probabilità che un errore umano rovini la ricerca è molto più alta. Una volta che ho cominciato ad automatizzare i miei test, ho ottenuto risultati molto migliori.</para>
Giulio@757 522
Giulio@757 523 <para id="x_14e">La chiave del collaudo automatizzato è duplice:</para>
Giulio@757 524 <itemizedlist>
Giulio@757 525 <listitem><para id="x_14f">verificate sempre lo stesso sintomo, e</para>
Giulio@757 526 </listitem>
Giulio@757 527 <listitem><para id="x_150">fornite sempre informazioni consistenti al comando <command role="hg-cmd">hg bisect</command>.</para>
Giulio@757 528 </listitem></itemizedlist>
Giulio@757 529 <para id="x_151">Nel mio esempio precedente, il comando <command>grep</command> verifica il sintomo e l'istruzione <literal>if</literal> prende il risultato di questo controllo e si assicura di fornire la stessa informazione al comando <command role="hg-cmd">hg bisect</command>. La funzione <literal>mytest</literal> ci permette di riprodurre insieme queste due operazioni, in modo che ogni test sia uniforme e consistente.</para>
Giulio@757 530
Giulio@757 531 </sect2>
Giulio@757 532 <sect2>
Giulio@757 533 <title>Controllare i vostri risultati</title>
Giulio@757 534
Giulio@757 535 <para id="x_152">Dato che il risultato di una ricerca con <command role="hg-cmd">hg bisect</command> è solo tanto buona quanto le informazioni che passate al comando, non prendete il changeset che vi indica come la verità assoluta. Un modo semplice di effettuare un riscontro sul risultato è quello di eseguire manualmente i vostro test su ognuno dei changeset seguenti.</para>
Giulio@757 536 <itemizedlist>
Giulio@757 537 <listitem><para id="x_153">Il changeset che il comando riporta come la prima revisione guasta. Il vostro test dovrebbe verificare che la revisione è effettivamente guasta.</para>
Giulio@757 538 </listitem>
Giulio@757 539 <listitem><para id="x_154">Il genitore di quel changeset (entrambi i genitori, se è un'unione). Il vostro test dovrebbe verificare che quel changeset è corretto.</para>
Giulio@757 540 </listitem>
Giulio@757 541 <listitem><para id="x_155">Un figlio di quel changeset. Il vostro test dovrebbe verificare che quel changeset è guasto.</para>
Giulio@757 542 </listitem></itemizedlist>
Giulio@757 543
Giulio@757 544 </sect2>
Giulio@757 545 <sect2>
Giulio@757 546 <title>Fate attenzione alle interferenze tra i bug</title>
Giulio@757 547
Giulio@757 548 <para id="x_156">&Egrave; possibile che la vostra ricerca di un bug venga rovinata dalla presenza di un altro bug. Per esempio, diciamo che il vostro software ~crashes~ alla revisione 100 e funziona correttamente alla revisione 50. Senza che voi lo sappiate, qualcun altro ha introdotto un ~crashing~ bug differente alla revisione 60 e lo ha corretto alla revisione 80. Questo potrebbe distorcere i vostri risultati in vari modi.</para>
Giulio@757 549
Giulio@757 550 <para id="x_157">&Egrave; possibile che questo altro bug <quote>mascheri</quote> completamente il vostro, cioè che sia comparso prima che il vostro bug abbia avuto la possibilità di manifestarsi. Se non potete evitare quell'altro bug (per esempio, impedisce al vostro progetto di venire assemblato) e quindi non potete dire se il vostro bug è presente in un particolare changeset, il comando <command role="hg-cmd">hg bisect</command> non è in grado di aiutarvi direttamente. Invece, invocando <command role="hg-cmd">hg bisect --skip</command> potete contrassegnare un changeset come non collaudato.</para>
Giulio@757 551
Giulio@759 552 <para id="x_158">Potrebbe esserci un problema differente se il vostro test per la presenza di un bug non è abbastanza specifico. Se controllate che <quote>il mio programma ~crashes~</quote>, allora sia il vostro ~crashing~ bug che il ~crashing~ bug scorrelato che lo maschera sembreranno la stessa cosa e fuorvieranno <command role="hg-cmd">hg bisect</command>.</para>
Giulio@757 553
Giulio@757 554 <para id="x_159">Un'altra situazione utile in cui sfruttare <command role="hg-cmd">hg bisect --skip</command> è quella in cui non potete collaudare una revisione perché il vostro progetto era guasto e quindi in uno stato non collaudabile in quella revisione, magari perché qualcuno aveva introdotto un cambiamento che impediva al progetto di venire assemblato.</para>
Giulio@757 555
Giulio@757 556 </sect2>
Giulio@757 557 <sect2>
Giulio@757 558 <title>Circoscrivete la vostra ricerca in maniera ritardata</title>
Giulio@757 559
Giulio@757 560 <para id="x_15a">Scegliere il primo changeset <quote>corretto</quote> e il primo changeset <quote>guasto</quote> che contrassegneranno i punti estremi della vostra ricerca è spesso facile, ma merita comunque una breve discussione. Dal punto di vista di <command role="hg-cmd">hg bisect</command>, il changeset <quote>più recente</quote> è convenzionalmente <quote>guasto</quote> e il changeset <quote>più vecchio</quote> è <quote>corretto</quote>.</para>
Giulio@757 561
Giulio@757 562 <para id="x_15b">Se avete problemi a ricordare dove si trova un changeset <quote>corretto</quote> da fornire al comando <command role="hg-cmd">hg bisect</command>, non potreste fare meglio che collaudare changeset a caso. Ricordatevi di eliminare i contendenti che non possono esibire il bug (magari perché la funzionalità con il bug non era ancora presente) e quelli in cui un altro problema nasconde il bug (come ho discusso in precedenza).</para>
Giulio@757 563
Giulio@757 564 <para id="x_15c">Anche se i vostri tentativi si concludono <quote>in anticipo</quote> di migliaia di changeset o di mesi di cronologia, aggiungerete solo una manciata di test al numero totale che <command role="hg-cmd">hg bisect</command> deve eseguire, grazie al suo comportamento logaritmico.</para>
Giulio@757 565
Giulio@757 566 </sect2>
Giulio@757 567 </sect1>
Giulio@757 568 </chapter>