# HG changeset patch # User Giulio@puck # Date 1247952480 -7200 # Node ID 09b28667b750e5213902297bf74da497f2c7617a # Parent 2ee9e03eddce7149578a7b714fa96d3fc436293d Literal translation of Ch.9. diff -r 2ee9e03eddce -r 09b28667b750 it/00book.xml --- a/it/00book.xml Fri Jul 17 18:53:35 2009 +0200 +++ b/it/00book.xml Sat Jul 18 23:28:00 2009 +0200 @@ -15,8 +15,8 @@ + &ch08; - + + &ch09; FIXME per una discussione sul comando patch). 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 patch non è in grado di farlo. + + + + + Modifiche che non avrebbero mai dovuto essere fatte + + Quasi sempre, il comando hg backout è 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. + + 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 grandi, quindi aumentano la dimensione del repository e il tempo necessario a clonarlo o a estrarne i cambiamenti. + + Prima di illustrare le opzioni che avete se eseguite il commit di un cambiamento da sacchetto di carta marrone (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. + + 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 hg rollback, come descritto nella . + + Dopo che avete trasmesso un cambiamento sbagliato a un altro repository, potreste anocra usare hg rollback 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. + + Se vi trovate in una situazione come questa e sapete verso quali repository si è propagato il vostro cambiamento sbagliato, potete provare a sbarazzarvi del cambiamento in ognuno di quei repository. Questa, naturalmente, non è una soluzione soddisfacente: se mancate anche un singolo repository quando state ripulendo, il cambiamento sarà ancora là fuori e potrebbe propagarsi ulteriormente. + + Se avete inserito uno o più cambiamenti dopo il cambiamento che vorreste veder sparire, le vostre opzioni si riducono ulteriormente. Mercurial non offre alcun modo per fare un buco nella cronologia lasciando gli altri changeset intatti. + + + Ritirare un'unione + + 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. + + 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 hg backout può aiutare a rendere la pulizia più semplice. Offre un'opzione , che vi consente di specificare a quale genitore tornare quando state ritirando un'unione. + +
+ Un'unione sbagliata + + + XXX add text + +
+ + Supponete di avere un grafo delle revisioni simile a quello della . Quello che vorremmo fare è rifare l'unione tra le revisioni 2 e 3. + + Potremmo eseguire questa operazione nel modo seguente. + + + + Invocare hg backout --rev=4 --parent=2. Questo dice al comando hg backout 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 . +
+ Ritirare l'unione favorendo un genitore + + + XXX add text + +
+
+ + + Invocare hg backout --rev=4 --parent=3. Questo dice al comando hg backout di ritirare ancora la revisione 4, ma questa volta scegliendo il genitore 3, l'altro genitore dell'unione. Il risultato è visibile nella , in cui il repository ora contiene tre teste. +
+ Ritirare l'unione favorendo l'altro genitore + + + XXX add text + +
+
+ + + 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 . +
+ Unire i risultati dei ritiri + + + XXX add text + +
+
+ + + Eseguire un'unione con il commit che è stato eseguito dopo l'unione sbagliata, come mostrato nella . +
+ Unire i risultati dei ritiri + + + XXX add text + +
+
+
+
+ + + Proteggervi dai cambiamenti che vi sono <quote>sfuggiti</quote> + + 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. + + Configurando alcuni hook su quel repository per validare i changeset in entrata (si veda il ), 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 estinguersi perché non possono propagarsi verso il repository centrale. Ancora meglio, questo accade senza alcun bisogno di un intervento esplicito. + + Per esempio, un hook sui cambiamenti in entrata che verifica che un changeset si riesca effettivamente a compilare può prevenire involontari guasti al processo di assemblaggio. + + + + Cosa fare con i cambiamenti sensibili che sfuggono + + 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. + + 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. + + 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. + + 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. + + 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 fa sparire questo file 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. + +
+ + + Trovare la causa di un bug + + 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 hg bisect, che vi aiuta ad automatizzare questo processo e a completarlo in maniera molto efficiente. + + L'idea dietro al comando hg bisect è 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 hg bisect usa il vostro test per dirigere la propria ricerca del changeset che ha introdotto il codice che ha causato il bug. + + Ecco alcuni scenari per aiutarvi a capire come potreste applicare questo comando. + + 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. + + 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. + + 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 è veloce o lento. + + La dimensione dei componenti del progetto che rilasciate è esplosa di recente e sospettate che qualcosa sia cambiato nel modo in cui assemblate il progetto. + + + Da questi esempi, dovrebbe essere chiaro che il comando hg bisect non è utile solo per trovare le cause dei bug. Potete usarlo per trovare qualsiasi proprietà emergente 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. + + 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 test è qualcosa che voi eseguite quando hg bisect sceglie un changeset. Una sonda è ciò che hg bisect esegue per dirvi se una revisione è buona. Infine, useremo la parola bisezione come nome e bisezionare come verbo per intendere la ricerca tramite il comando hg bisect. + + 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 giorni 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. + + Il comando hg bisect invece usa la propria conoscenza della forma della cronologia delle revisioni del vostro progetto per effettuare una ricerca in tempo proporzionale al logaritmo 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). + + Il comando hg bisect è consapevole della natura ramificata 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. + + + Usare il comando <command role="hg-cmd">hg bisect</command> + + Ecco un esempio di hg bisect in azione. + + + Fino alla versione 0.9.5 di Mercurial compresa, hg bisect 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. + + + Ora creiamo un nuovo repository in modo che possiate provare il comando hg bisect in isolamento. + + &interaction.bisect.init; + + 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 bug. Questo ciclo crea 35 changeset, ognuno dei quali aggiunge un singolo file al repository. Rappresenteremo il nostro bug con un file che contiene il testo i have a gub. + + &interaction.bisect.commits; + + La prossima cosa che vorremmo fare è capire come usare il comando hg bisect. Possiamo usare il normale meccanismo di aiuto predefinito di Mercurial per fare questo. + + &interaction.bisect.help; + + Il comando hg bisect lavora in più passi. Ogni passo procede nella maniera seguente. + + Eseguite il vostro test binario. + + Se il test ha avuto successo, informate hg bisect invocando il comando hg bisect --good. + + Se il test è fallito, invocate il comando hg bisect --bad. + + Il comando usa le vostre informazioni per decidere quale changeset collaudare successivamente. + + Il comando aggiorna la directory di lavoro a quel changeset e il processo ricomincia da capo. + + Il processo termina quando hg bisect identifica un unico cambiamento che contrassegna il punto in cui il vostro test passa dallo stato di successo a quello di fallimento. + + Per cominciare la ricerca, dobbiamo eseguire il comando hg bisect --reset. + + &interaction.bisect.search.init; + + Nel nostro caso, il test binario che usiamo è semplice: controlliamo per vedere se qualche file nel repository contiene la stringa i have a gub. Se è così, questo changeset contiene il cambiamento che ha causato il bug. Per convenzione, un changeset che ha la proprietà che stiamo cercando è guasto, mentre uno che non ce l'ha è corretto. + + 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 guasta. + + &interaction.bisect.search.bad-init; + + Il nostro compito successivo consiste nel nominare un changeset che sappiamo non contenere il bug, in modo che il comando hg bisect possa circoscrivere 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 corretto più avanti.) + + &interaction.bisect.search.good-init; + + Notate che questo comando ha stampato alcune informazioni. + + Ci ha detto quanti changeset deve considerare prima di poter identificare quello che ha introdotto il bug e quanti test saranno richiesti dal processo. + + Ha aggiornato la directory di lavoro al prossimo changeset da collaudare e ci ha detto quale changeset sta collaudando. + + + Ora eseguiamo il nostro test nella directory di lavoro, usando il comando grep per vedere se il nostro file guasto è presente. Se c'è, questa revisione è guasta, altrimenti è corretta. + + &interaction.bisect.search.step1; + + Questo test sembra un perfetto candidato per l'automazione, quindi trasformiamolo in una funzione di shell. + + &interaction.bisect.search.mytest; + + Ora possiamo eseguire un intero passo di collaudo con il singolo comando mytest. + + &interaction.bisect.search.step2; + + Ancora qualche altra invocazione del comando che abbiamo preparato per il passo di collaudo e abbiamo finito. + + &interaction.bisect.search.rest; + + Anche se avevamo 40 changeset attraverso cui cercare, il comando hg bisect ci ha permesso di trovare il changeset che ha introdotto il nostro bug usando solo cinque test. Dato che il numero di test effettuati dal comando hg bisect cresce con il logaritmo del numero dei changeset da analizzare, il vantaggio che ha rispetto a una ricerca che usa la strategia della forza bruta aumenta con ogni changeset che aggiungete. + + + + Riordinare dopo la vostra ricerca + + Quando avete finito di usare il comando hg bisect in un repository, potete invocare il comando hg bisect --reset 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, hg bisect non vi permetterà di cominciare una nuova ricerca in quel repository fino a quando non avrete eseguito hg bisect --reset. + + &interaction.bisect.search.reset; + + + + + Suggerimenti per trovare efficacemente i bug + + + Fornire informazioni consistenti + + Il comando hg bisect 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, potrebbe 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. + + + + Automatizzare il più possibile + + Quando ho cominciato a usare il comando hg bisect, 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. + + I miei problemi iniziali nel guidare a mano il comando hg bisect 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 hg bisect 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. + + La chiave del collaudo automatizzato è duplice: + + verificate sempre lo stesso sintomo, e + + fornite sempre informazioni consistenti al comando hg bisect. + + Nel mio esempio precedente, il comando grep verifica il sintomo e l'istruzione if prende il risultato di questo controllo e si assicura di fornire la stessa informazione al comando hg bisect. La funzione mytest ci permette di riprodurre insieme queste due operazioni, in modo che ogni test sia uniforme e consistente. + + + + Controllare i vostri risultati + + Dato che il risultato di una ricerca con hg bisect è 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. + + Il changeset che il comando riporta come la prima revisione guasta. Il vostro test dovrebbe verificare che la revisione è effettivamente guasta. + + Il genitore di quel changeset (entrambi i genitori, se è un'unione). Il vostro test dovrebbe verificare che quel changeset è corretto. + + Un figlio di quel changeset. Il vostro test dovrebbe verificare che quel changeset è guasto. + + + + + Fate attenzione alle interferenze tra i bug + + È 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. + + È possibile che questo altro bug mascheri 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 hg bisect non è in grado di aiutarvi direttamente. Invece, invocando hg bisect --skip potete contrassegnare un changeset come non collaudato. + + Potrebbe esserci un problema differente se il vostro test per la presenza di un bug non è abbastanza specifico. Se controllate che il mio programma ~crashes~, allora sia il vostro ~crashing~ bug che un ~crashing~ bug non correlato che lo maschera sembreranno la stessa cosa e fuorvieranno hg bisect. + + Un'altra situazione utile in cui sfruttare hg bisect --skip è 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. + + + + Circoscrivete la vostra ricerca in maniera ritardata + + Scegliere il primo changeset corretto e il primo changeset guasto che contrassegneranno i punti estremi della vostra ricerca è spesso facile, ma merita comunque una breve discussione. Dal punto di vista di hg bisect, il changeset più recente è convenzionalmente guasto e il changeset più vecchio è corretto. + + Se avete problemi a ricordare dove si trova un changeset corretto da fornire al comando hg bisect, 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). + + Anche se i vostri tentativi si concludono in anticipo di migliaia di changeset o di mesi di cronologia, aggiungerete solo una manciata di test al numero totale che hg bisect deve eseguire, grazie al suo comportamento logaritmico. + + + +