# HG changeset patch # User Giulio@puck # Date 1248889497 -7200 # Node ID 87d7000115f86aca7148f3708dd490e40f178fe7 # Parent 9cf5dbc6a9d7c0c28286d17e4fa3df7a1cced33d Literal translation of Ch.12. diff -r 9cf5dbc6a9d7 -r 87d7000115f8 it/00book.xml --- a/it/00book.xml Mon Jul 27 22:08:15 2009 +0200 +++ b/it/00book.xml Wed Jul 29 19:44:57 2009 +0200 @@ -18,8 +18,8 @@ + @@ -109,8 +109,8 @@ &ch10; &ch11; - + + &ch12; diff -r 9cf5dbc6a9d7 -r 87d7000115f8 it/ch12-mq.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/it/ch12-mq.xml Wed Jul 29 19:44:57 2009 +0200 @@ -0,0 +1,553 @@ + + + Gestire il cambiamento con Mercurial Queues + + + Il problema della gestione delle patch + + Ecco uno scenario comune: avete bisogno di installare un pacchetto software dai sorgenti, ma trovate un bug che dovete correggere nei sorgenti prima di poter cominciare a usare il pacchetto. Fate le vostre modifiche, vi dimenticate del pacchetto per un po' e alcuni mesi dopo avete bisogno di aggiornare il pacchetto a una nuova versione. Se la versione più nuova del pacchetto ha ancora il bug, dovete estrarre la vostra correzione dal vecchio albero dei sorgenti e applicarla alla nuova versione. Questa è un'attività seccante ed è facile commettere errori. + + Questo è un semplice caso del problema di gestione delle patch. Avete un albero di sorgenti a monte che non potete cambiare; avete bisogno di fare alcune modifiche locali all'albero a monte; e vi piacerebbe essere in grado di mantenere separate quelle modifiche, in modo da poterle applicare a nuove versioni della sorgente a monte. + + Il problema di gestione delle patch si presenta in molte situazioni. Probabilmente la più visibile è quella in cui un utente di un progetto software open source contribuisce la correzione di un bug o una nuova funzione ai manutentori del progetto sotto forma di patch. + + I distributori di sistemi operativi che includono software open source hanno spesso bisogno di effettuare modifiche ai pacchetti che distribuiscono in modo da assemblarli correttamente nei propri ambienti. + + Quando avete alcune modifiche da mantenere, è facile gestire una singola patch usando i programmi standard diff e patch (si veda la per una discussione di questi strumenti). Una volta che il numero di modifiche cresce, comincia ad avere senso l'idea di mantenere le patch come frammenti di lavoro distinti, in modo che per esempio una singola patch conterrà solo una correzione di bug (la patch potrebbe modificare diversi file, ma sta facendo solo una cosa) e potreste avere un certo numero di queste patch per bug differenti che dovete correggere e modifiche locali di cui avete bisogno. In questa situazione, se sottoponete una patch per la correzione di un bug ai manutentori del pacchetto a monte e questi includono la vostra correzione in una release successiva, potete semplicemente scartare quella singola patch quando state aggiornando a una nuova release. + + Mantenere una singola patch per un albero a monte è un compito un po' fastidioso e soggetto a errori, ma non è difficile. Tuttavia, la complessità del problema cresce rapidamente man mano che il numero di patch che dovete mantenere aumenta. Con più di un piccolo numero di patch in mano, cpaire quali dovete applicare e mantenerle passa da confuso a opprimente. + + Fortunatamente, Mercurial include una potente estensione, chiamata Mercurial Queues (letteralmente, Code di Mercurial) o semplicemente MQ, che semplifica grandemente il problema di gestione delle patch. + + + + La preistoria di Mercurial Queues + + Verso la fine degli anni '90, diversi sviluppatori del kernel di Linux cominciarono a mantenere alcune serie di patch che modificavano il comportamento del kernel di Linux. Alcune di queste serie si concentravano sulla stabilità, alcune sulla copertura di funzionalità e altre erano più sperimentali. + + La dimensinoe di queste serie di patch crebbe rapidamente. Nel 2002, Andrew Morton pubblicò alcuni script di shell che aveva usato per automatizzare la gestione delle proprie code di patch. Andrew era riuscito a usare questi script per gestire centinaia (talvolta migliaia) di patch per il kernel di Linux. + + + Una coperta a scacchi + + All'inizio del 2003, Andreas Gruenbacher and Martin Quinson presero in prestito l'approccio degli script di Andrew e pubblicarono uno strumento chiamato patchwork quilt (letteralmente, coperta a scacchi) web:quilt, o semplicemente quilt (si veda gruenbacher:2005 per un articolo che lo descrive). Dato che quilt sostanzialmente automatizzava la gestione delle patch, guadagnò rapidamente un grande seguito tra gli sviluppatori di software open source. + + Quilt gestisce una pila di patch per un albero di directory. Per cominciare, dovete dire a quilt di gestire un albero di directory e quali file volete che gestisca; esso memorizza i nomi e il contenuto di quei file. Per correggere un bug, create una nuova patch (usando un singolo comando), modificate i file che dovete correggere, poi aggiornate la patch. + + L'operazione di aggiornamento induce quilt a esaminare l'albero di directory; aggiorna la patch con tutte le modifiche che avete fatto. Potete creare un'altra patch in aggiunta alla prima, che terrà traccia dei cambiamenti richiesti per modificare l'albero da albero con una patch applicata ad albero con due patch applicate. + + Potete cambiare quali patch sono applicate all'albero. Se estraete una patch, i cambiamenti effettuati da quella patch spariranno dall'albero di directory. Quilt si ricorda quali patch avete estratto, comunque, così potete inserire nuovamente una patch estratta e l'albero di directory verrà ripristinato per contenere le modifiche in quella patch. La cosa più importante è che potete eseguire il comando di aggiornamento in ogni momento, e la patch applicata più recentemente verrà aggiornata. Questo significa che, in ogni momento, potete cambiare sia quali patch sono applicate sia quali modifiche vengono effettuate da quelle patch. + + Quilt non sa nulla di strumenti di controllo di revisione, così funziona altrettanto bene con un archivio estratto che con una copia di lavoro di Subversion. + + + + Da patchwork quilt a Mercurial Queues + + A metà del 2005, Chris Mason prese le funzionalità di quilt e implementò un'estensione che chiamò Mercurial Queues, che aggiungeva a Mercurial un comportamento simile a quello di quilt. + + La differenza chiave tra quilt e MQ è che quilt non sa nulla di sistemi di controllo di revisione, mentre MQ è integrata in Mercurial. Ogni patch che inserite è rappresentata sotto forma di changeset Mercurial. Estraete una patch, e il changeset sparisce. + + Dato che quilt non si preoccupa degli strumenti di controllo di revisione, rimane un software tremendamente utile da conoscere per impiegarlo nelle situazioni in cui non potete usare Mercurial e MQ. + + + + + L'enorme vantaggio di MQ + + Non posso esagerare il valore che MQ offre attraverso l'unificazione di patch e controllo di revisione. + + Una delle ragioni principali per cui le patch hanno continuato a venire usate nel mondo del software libero e open source&emdash;nonostante la disponibilità di strumenti di controllo di revisione sempre più capaci attraverso gli anni&emdash; è l'agilità che offrono. + + I tradizionali strumenti di controllo di revisione effettuano una registrazione permanente di ogni cosa che fate. Mentre questo ha un grande valore, è anche piuttosto soffocante. Se volete effettuare un esperimento allucinato, dovete stare molto attenti a come procedete, o rischiate di lasciare tracce inutili&emdash;o peggio, ingannevoli e destabilizzanti&emdash; dei vostri #missteps# e dei vostri errori nella registrazione permanente delle revisioni. + + Al contrario, il matrimonio tra controllo di revisione distribuito e patch realizzato da MQ rende molto più facile isolare il vostro lavoro. Le vostre patch si basano sulla normale cronologia delle revisioni e potete farle sparire e comparire a piacere. Se non vi piace una patch, potete scartarla. Se una patch non è esattamente come volete che sia, vi basta correggerla&emdash;tutte le volte che ne avete bisogno, fino a quando non l'avete affinata nella forma che desiderate. + + Per esempio, l'integrazione delle patch con il controllo di revisione rende la comprensione delle patch&emdash;e delle interazioni con il codice su cui si basano&emdash;e la correzione dei loro effetti enormemente più facile. Dato che ogni patch applicata ha un changeset associato, potete invocare hg log con un nome di file per vedere quali changeset e quali patch hanno avuto effetto sul file. Potete usare il comando hg bisect per condurre una ricerca binaria attraverso tutti i changeset e le patch applicate per vedere dove un bug è stato introdotto o corretto. Potete usare il comando hg annotate per vedere quali changeset o patch hanno modificato una particolare riga di un file sorgente. E così via. + + + + Capire le patch + + Dato che MQ non nasconde la sua natura orientata alle patch, è utile capire cosa sono le patch e conoscere un po' gli strumenti che lavorano con esse. + + Il tradizionale comando Unix diff confronta due file e stampa una lista di differenze tra loro. Il comando patch interpreta queste differenze come modifiche da effettuare a un file. Date un'occhiata qui di seguito per vedere un semplice esempio di questi comandi in azione. + + &interaction.mq.dodiff.diff; + + Il tipo di file generato da diff (e che patch prende in ingresso) viene chiamato una patch o un diff; non c'è alcuna differenza tra una patch e un diff. (Noi useremo il termine patch, dato che è quello più comunemente usato.) + + Un file di patch può cominciare con testo arbitrario; il comando patch ignora questo testo, ma MQ lo usa come messaggio di commit quando crea i changeset. Per trovare l'inizio del contenuto della patch, patch cerca la prima riga che comincia con la stringa diff -. + + MQ lavora con i diff in formato unified (patch può accettare molti altri formati di diff, ma MQ no). Un diff in formato unified contiene due tipi di intestazione. L'intestazione di file descrive il file che viene modificato e contiene il nome del file da modificare. Quando patch vede una nuova intestazione di file, cerca il file con quel nome per cominciare a modificarlo. + + L'intestazione di file è seguita da una serie di blocchi. Ogni blocco comincia con un'intestazione che identifica l'intervallo di numeri di riga nel file che il blocco dovrebbe modificare. Dopo l'intestazione, un blocco comincia e finisce con alcune (di solito tre) righe di testo dal file originale che vengono chiamate il contesto del blocco. Se c'è solo una quantità ridotta di contesto tra blocchi successivi, diff non stampa una nuova intestazione, ma si limita a unire i blocchi insieme, con alcune righe di contesto tra le modifiche. + + Ogni riga di contesto comincia con un carattere di spazio. Nell'ambito di un blocco, una riga che comincia con - significa rimuovi questa riga, mentre una riga che comincia con + significa inserisci questa riga. Per esempio, una riga modificata viene rappresentata da una cancellazione e un inserimento. + + Ritorneremo ad alcuni degli aspetti più sottili delle patch più tardi (nella ), ma ora dovreste avere abbastanza informazioni per usare MQ. + + + + Cominciare a usare Mercurial Queues + + Dato che MQ è implementata come un'estensione, dovete esplicitamente abilitarla prima di poterla suare. (Non avete bisogno di scaricare nulla, perché MQ è inclusa con la distribuzione standard di Mercurial.) Per abilitare MQ, modificate il vostro file ~/.hgrc aggiungendo le seguenti righe. + + [extensions] +hgext.mq = + + Una volta che avete abilitato l'estensione, vi verranno messi a disposizione alcuni nuovi comandi. Per verificare che l'estensione funzioni, potete usare hg help per vedere se il comando qinit viene elencato come disponibile. + + &interaction.mq.qinit-help.help; + + Potete usare MQ con qualsiasi repository Mercurial, e i suoi comandi operano solo all'interno di quel repository. Per cominciare, preparate semplicemente il repository usando il comando qinit command. + + &interaction.mq.tutorial.qinit; + + Questo comando crea una directory vuota chiamata .hg/patches, dove MQ terrà i propri metadati. Come accade con molti comandi Mercurial, il comando qinit non stamperà nulla nel caso termini con successo. + + + Creare una nuova patch + + Per cominciare a lavorare su una nuova patch, usate il comando qnew. Questo comando prende un argomento, il nome della patch da creare. + + MQ userà questo nome come il nome di un file che memorizzerà nella directory .hg/patches, come potete vedere qui sotto. + + &interaction.mq.tutorial.qnew; + + La directory .hg/patches contiene anche altri due nuovi file, series e status. Il file series elenca tutte le patch che MQ conosce per questo repository, con una patch per riga. Mercurial usa il file status per tenere traccia internamente di tutte le patch che MQ ha applicato a questo repository. + + + Talvolta, potreste voler modificare il file series a mano, per esempio per cambiare la sequenza in cui sono applicate alcune patch. Tuttavia, modificare manualmente il file status è quasi sempre una cattiva idea, dato che è facile rovinare l'idea di MQ di quello che sta succedendo. + + + Una volta che avete creato la vostra nuova patch, potete modificare i file nella directory di lavoro come fareste di solito. Tutti i normali comandi Mercurial, come hg diff e hg annotate, funzionano allo stesso modo in cui funzionavano prima. + + + + Aggiornare una patch + + Quando raggiungete un punto in cui volete salvare il vostro lavoro, usate il comando qrefresh per aggiornare la patch su cui state lavorando. + + &interaction.mq.tutorial.qrefresh; + + Questo comando include nella vostra patch i cambiamenti che avete fatto nella directory di lavoro e aggiorna il changeset corrispondente alla patch in modo che contenga quei cambiamenti. + + Potete eseguire qrefresh tutte le volte che volete, quindi è un buon modo per controllare il vostro lavoro. Aggiornate la vostra patch al momento opportuno, tentate un esperimento e se l'esperimento non funziona usate hg revert per ripristinare le vostre modifiche all'ultimo aggiornamento che avete compiuto. + + &interaction.mq.tutorial.qrefresh2; + + + + Impilare e registrare le patch + + Una volta che avete finito di lavorare su una patch, o avete bisogno di lavorare su un'altra patch, potete usare di nuovo il comando qnew per creare una nuova patch. Mercurial applicherà questa patch a partire dalla vostra patch esistente. + + &interaction.mq.tutorial.qnew2; + + Notate che la patch contiene le modifiche nella nostra patch precedente come parte del proprio contesto (potete vederlo più chiaramente nell'output di hg annotate). + + Finora, con l'eccezione di qnew e qrefresh, siamo stati attenti a usare solo gli ordinari comandi Mercurial. Tuttavia, MQ fornisce molti comandi che sono più facili da usare quando state pensando in termini di patch, come illustrato di seguito. + + &interaction.mq.tutorial.qseries; + + + Il comando qseries elenca tutte le patch per questo repository note a MQ, dalla più vecchia alla più recente (più recentemente creata). + + Il comando qapplied elenca tutte le patch che MQ ha applicato a questo repository, ancora dalla più vecchia alla più recente (più recentemente applicata). + + + + + Manipolare la pila delle patch + + La discussione precedente implica che ci deve essere una differenza tra patch note e patch applicate, e in effetti c'è. MQ può gestire una patch senza che sia applicata al repository. + + Una patch applicata ha un corrispondente changeset nel repository e gli effetti della patch e del changeset sono visibili nella directory di lavoro. Potete annullare l'applicazione di una patch usando il comando qpop. La patch estratta è ancora nota, o gestita, da MQ, ma non ha più un corrispondente changeset nel repository, e la directory di lavoro non contiene più le modifiche apportate dalla patch. La illustra la differenza tra patch applicate e registrate. + +
+ Patch applicate e non applicate nella pila delle patch di MQ + + + XXX add text + +
+ + Potete riapplicare una patch non applicata o estratta usando il comando qpush. Questo crea un nuovo changeset da far corrispondere alla patch, e le modifiche della patch diventano ancora una volta presenti nella directory di lavoro. Di seguito, vengono mostrati esempi di qpop e qpush in azione. + + &interaction.mq.tutorial.qpop; + + Notate che una volta che abbiamo estratto una patch o due patch, l'output di qseries rimane lo stesso, mentre quello di qapplied è cambiato. + +
+ + + Inserire ed estrarre molte patch + + Mentre ognuno dei comandi qpush e qpop opera in maniera predefinita su una singola patch alla volta, potete inserire ed estrarre molte patch in un unico passaggio. L'opzione di qpush lo induce a inserire tutte le patch non applicate, mentre l'opzione di qpop lo induce a estrarre tutte le patch applicate. (Per ulteriori modi di inserire ed estrarre molte patch, si veda la più avanti.) + + &interaction.mq.tutorial.qpush-a; + + + + I controlli di sicurezza, e come #overriding them# + + Diversi comandi MQ controllano la directory di lavoro prima di fare qualunque cosa e falliscono se trovano una qualsiasi modifica. Si comportano in questo modo per assicurarsi che non perdiate alcun cambiamento che avete fatto ma che non avete ancora incorporato in una patch. L'esempio seguente illustra questo caso: il comando qnew eviterà di creare una nuova patch se ci sono cambiamenti in sospeso, causati in questo caso dall'invocazione di hg add su file3. + + &interaction.mq.tutorial.add; + + Tutti i comandi che controllano la directory di lavoro accettano un'opzione so cosa sto facendo che si chiama sempre . L'esatto significato di dipende dal comando. Per esempio, hg qnew incorporerà i cambiamenti in sospeso nella nuova patch creata, ma hg qpop annullerà le modifiche a qualsiasi file affetto dalla patch che sta estraendo. Assicuratevi di leggere la documentazione per l'opzione di un comando prima di usarla! + + + + Lavorare su diverse patch alla volta + + Il comando qrefresh aggiorna sempre la patch applicata in cima alla pila. Questo significa che potete sospendere il lavoro su una patch (aggiornandola), estrarre o inserire per posizionare una patch differente in cima alla pila e lavorare su quella patch per un po'. + + Ecco un esempio che illustra come potete sfruttare questa possibilità. Diciamo che state sviluppando una nuova funzione sotto forma di due patch. La prima è una modifica al nucleo del vostro software e la seconda&emdash;basata sulla prima&emdash;modifica l'interfaccia utente per usare il codice che avete appena aggiunto al nucleo. Se notate un bug nel nucleo mentre state lavorando sulla patch per l'interfaccia utente, è facile correggere il nucleo. Vi basta usare qrefresh per salvare le modifiche in corso alla vostra patch di interfaccia ed estrarre tramite qpop la patch del nucleo. Correggete il bug nel nucleo, aggiornate la patch del nucleo con qrefresh e inserite la patch di interfaccia tramite qpush per continuare a lavorare dal punto dove avevate lasciato. + +
+ + + Ulteriori informazioni sulle patch + + MQ usa il comando GNU patch per applicare le patch, quindi è utile conoscere qualche altro aspetto dettagliato sul funzionamento di patch e sulle patch stesse. + + + #The strip count# + + Se guardate alle intestazioni di file in una patch, noterete che i percorsi dei nomi di solito hanno un componente aggiuntivo iniziale che non è presente nel percorso reale. Questo è un rimasuglio del modo in cui le persone erano solite generare le patch (viene ancora fatto in questo modo, ma è piuttosto raro con i moderni strumenti di controllo di revisione). + + Alice estrarrebbe un archivio, modificherebbe i file e poi deciderebbe di voler creare una patch. Quindi rinominerebbe della directory di lavoro, estrarrebbe l'archivio nuovamente (da qui nasce il bisogno di modificare il nome) e userebbe le opzioni e del comando diff per generare ricorsivamente una patch tra la directory non modificata e quella modificata. Il risultato sarebbe che il nome della directory non modificata si troverebbe all'inizio del percorso sulla parte destra in ogni intestazione di file e il nome della directory modificata si troverebbe all'inizio del percorso sulla parte destra. + + Dato che chi riceve la patch dalle Alice della rete probabilmente non avrebbe le due copie, modificata e non, della directory, con esattamente gli stessi nomi, il comando patch ha un'opzione che indica il numero di elementi iniziali da eliminare dal percorso al momento di applicare una patch. Questo numero viene chiamato #strip count#. + + Un'opzione -p1 significa usa uno #strip count# di uno. Se patch vede un nome di file foo/bar/baz in un'intestazione di file, eliminerà foo e proverà ad applicare la patch al file bar/baz. (Strettamente parlando, lo #strip count# si riferisce al numero di separatori di percorso (e dei relativi elementi) da eliminare. Uno #strip count# pari a uno trasformerà foo/bar in bar, ma /foo/bar (notate lo slash iniziale) in foo/bar.) + + Lo #strip count# standard per le patch è pari a uno, in quanto quasi tutte le patch contengono un elemento iniziale da eliminare nel percorso. Il comando hg diff di Mercurial genera nomi di percorso in questa forma, e sia il comando hg import che MQ si aspettano patch con uno #strip count# pari a uno. + + Se qualcuno vi invia una patch che volete aggiungere alla vostra coda delle patch e la patch ha bisogno di uno #strip count# diverso da uno, non potete usare semplicemente qimport con la patch, perché qimport non è ancora dotato di un'opzione -p (si veda il problema 311). L'alternativa migliore che avete è quella di creare una vostra patch con qnew e poi usare patch -pN per applicare la patch che avete ricevuto, seguito da hg addremove per registrare qualsiasi file aggiunto o rimosso dalla patch, seguito da hg qrefresh. Questa complessità potrebbe diventare inutile; si veda il problema 311 per i dettagli. + + + + + Strategie per applicare una patch + + Quando patch applica un blocco, prova a impiegare una serie di strategie successive sempre meno accurate per fare in modo di applicare il blocco. Questa tecnica di #falling-back# spesso rende possibile prendere una patch che è stata generata da una vecchia versione di un file e applicarla alla nuova versione di quel file. + + Come prima cosa, patch cerca una corrispondenza esatta, dove i numeri di riga, il contesto e il testo da modificare devono applicarsi in maniera esatta. Se non può fare una corrispondenza esatta, cerca una corrispondenza esatta per il contesto, senza onorare le informazioni sulla numerazione delle righe. Se questa strategia ha successo, stampa una riga di output dicendo che il blocco è stato applicato, ma a una certa distanza dal numero di riga originale. + + Se la corrispondenza con il solo contesto fallisce, patch rimuove la prima e l'ultima riga del contesto e tenta una corrispondenza con la sola versione ridotta del contesto. Se il blocco con il contesto ridotto ha successo, stampa un messaggio dicendo di aver applicato il blocco con un fattore di incertezza (il numero dopo il fattore di incertezza indica quante righe del contesto sono state escluse da patch prima che la patch si potesse applicare). + + Quando nessuna di queste tecniche funziona, patch stampa un messaggio dicendo che il blocco in questione è stato rifiutato. Salva i blocchi rifiutati (anche chiamati semplicemente rifiuti) in un file con lo stesso nome e un'estensione .rej aggiuntiva. Salva anche le copie non modificate del file con un'estensione .orig; la copia del file senza alcuna estensione conterrà le modifiche fatte dal blocco che sono state effettivamente applicate. Se avete una patch che modifica il file foo con sei blocchi, e uno di essi non si riesce ad applicare, avrete: una copia foo.orig non modificata del file originale, un file foo.rej contenente un blocco e il file foo contenente le modifiche effettuate dai cinque blocchi applicati con successo. + + + + Alcune stranezze nella rappresentazione delle patch + + Ci sono alcune cose utili da sapere sul modo in cui patch lavora con i file. + + Questo dovrebbe già essere ovvio, ma patch non è in grado di gestire i file binari. + + Non si cura neanche del bit di esecuzione, bensì crea nuovi file come leggibili, ma non eseguibili. + + patch tratta la rimozione di un file come un diff tra il file da rimuovere e un file vuoto. Quindi la vostra idea di cancellare un file viene rappresentata in una patch come ogni riga di questo file è stata cancellata. + + Tratta l'aggiunta di un file come un diff tra un file vuoto e il file da aggiungere. Quindi la vostra idea di aggiungere un file viene rappresentata in una patch come ogni riga di questo file è stata aggiunta. + + Tratta un file rinominato come la rimozione del file con il vecchio nome e l'aggiunta del file con il nuovo nome. Questo significa che i file rinominati hanno una grande impronta sulle patch. (Notate anche che Mercurial attualmente non cerca di inferire se i file in una patch sono stati rinominati o copiati.) + + patch non è in grado di rappresentare i file vuoti, quindi non potete usare una patch per rappresentare la nozione di aggiungere questo vile vuoto all'albero. + + + + + + Fate attenzione all'incertezza + + Sebbene l'applicazione di un blocco a una certa distanza, o con un certo fattore di incertezza, avrà spesso un successo completo, queste tecniche inesatte lasciano naturalmente aperta la possibilità di rovinare il file modificato. Il caso più comune è tipicamente quello in cui la patch viene applicata due volte, o a una posizione sbagliata nel file. Se patch o qpush dovessero mai mezionare la distanza o il fattore di incertezza, dovreste assicurarvi che i file sono stati modificati in maniera corretta. + + Spesso è una buona idea aggiornare una patch che è stata applicata con una distanza o un fattore di incertezza; l'aggiornamento della patch genera nuove informazioni di contesto che permetteranno di applicarla in maniera pulita. Dico spesso, non sempre, perché qualche volta l'aggiornamento di una patch ne renderà impossibile l'applicazione su una revisione differente dei file coinvolti. In alcuni casi, come quando state mantenendo una patch che deve essere applicabile a versioni multiple di un albero di sorgenti, è considerato accettabile avere una patch che si applica con qualche incertezza, purché abbiate verificato i risultati del processo di applicazione in casi come questi. + + + + Gestire il rifiuto + + Se qpush non riesce ad applicare una patch, stamperà un messaggio di errore e terminerà. Se ha lasciato alcuni file .rej, normalmente è meglio correggere i blocchi rifiutati prima di inserire altre patch in cima alla pila o fare qualsiasi ulteriore modifica. + + Se la vostra patch era solita applicarsi in maniera pulita, e non lo fa più perché avete modificato il codice sottostante su cui le vostre patch si basavano, Mercurial Queues può aiutarvi; leggete la per i dettagli. + + Sfortunatamente, non c'è alcuna grande tecnica per gestire i blocchi rifiutati. Molto spesso, avrete bisogno di esaminare il file .rej e di modificare il file di destinazione, applicando a mano i blocchi rifiutati. + + Un programmatore del kernel di Linux, Chris Mason (l'autore di Mercurial Queues), ha realizzato uno strumento chiamato mpatch (http://oss.oracle.com/~mason/mpatch/), che adotta un approccio semplice per automatizzare l'applicazione dei blocchi rifiutati da patch. Il comando mpatch può aiutarvi nel caso il blocco sia stato rifiutato per quattro tipiche ragioni: + + + il contesto nel mezzo di un blocco è cambiato; + + all'inizio o alla fine del blocco manca una certa quantità di contesto; + + un blocco più ampio potrebbe applicarsi meglio&emdash;interamente o in parte&emdash;se fosse suddiviso in blocchi più piccoli; + + un blocco rimuove righe con un contesto leggermente differente rispetto a quello attualmente presente nel file. + + + + Se usate il comando mpatch, dovreste controllare doppiamente con attenzione i risultati quando avete finito. In effetti, mpatch impone questo metodo di doppio controllo sul risultato dello strumento, depositandovi automaticamente in un programma di gestione delle unioni quando ha concluso il proprio lavoro, in modo che possiate verificare i risultati e risolvere qualsiasi conflitto rimanente. + + + + + Ulteriori informazioni sulla gestione delle patch + + Man mano che acquisite familiarità con MQ, comincerete a voler eseguire altri tipi di operazioni di gestione delle patch. + + + Cancellare le patch non volute + + Se volete sbarazzarvi di una patch, usate il comando hg qdelete per cancellare il file contenente la patch e rimuovere la sua voce dalla serie di patch. Se provate a cancellare una patch che è ancora applicata, hg qdelete si rifiuterà di operare. + + &interaction.ch11-qdelete.go; + + + + Convertire verso e da revisioni permanenti + + Una volta che avete finito di lavorare con una patch e volete trasformarla in un changeset permanente, usate il comando hg qfinish. Passate una revisione al comando per identificare la patch che volete trasformare in un normale changeset, questa patch deve essere già stata applicate. + + &interaction.ch11-qdelete.convert; + + Il comando hg qfinish accetta una opzione o per trasformare tutte le patch applicate in normali changeset. + + È anche possibile trasformare un changeset esistente in una patch, passando l'opzione al comando hg qimport. + + &interaction.ch11-qdelete.import; + + Notate che ha senso convertire un changeset in una patch solo se non avete propagato quel changeset in altri repository. L'identificatore del changeset importato cambierà ogni volta che aggiornate la patch, cosa che indurrà Mercurial a trattarlo come se non fosse correlato al changeset originale che avete trasmesso da qualche altra parte. + + + + + Ottenere le prestazioni migliori da MQ + + MQ è molto efficiente nel gestire un grande numero di patch. Ho effettuato alcuni esperimenti sulle prestazioni a metà del 2006 per una presentazione che ho tenuto alla conferenza EuroPython 2006 (su macchine più moderne, dovreste aspettarvi risultati migliori di quelli che vedrete nel seguito). Come #data set# ho usato la serie di patch 2.6.17-mm1 per il kernel di Linux, contenente 1.738 patch. Ho applicato queste patch a un repository del kernel di Linux contenente tutte le 27.472 revisioni intercorse tra Linux 2.6.12-rc2 e Linux 2.6.17. + + Sul mio vecchio e lento portatile, sono riuscito a eseguire hg qpush per tutte le 1.738 patch in 3.5 minuti e a eseguire hg qpop per tutte le patch in 30 secondi. (Su portatili più recenti, il tempo per estrarre tutte le patch è sceso a due minuti.) Ho potuto aggiornare una delle patch più grandi (che ha effettuato 22.779 righe di cambiamenti a 287 file) eseguendo qrefresh in 6.6 secondi. + + Chiaramente, MQ è particolarmente adatto per lavorare su alberi di grandi dimensioni, ma ci sono alcuni trucchi che potete usare per ottenere pefrormance ancora migliori. + + Prima di tutto, provate a raggruppare insieme le operazioni. Ogni volta che eseguite qpush o qpop, questi comandi esaminano la directory di lavoro una volta per assicurarsi che non avete effettuato alcuna modifica dimenticandovi poi di invocare qrefresh. Su alberi di piccole dimensioni, il tempo impiegato da questa disamina è insignificante. Tuttavia, su un albero di medie dimensioni (contenente decine di migliaia di file), questa operazione può impiegare anche più di un secondo. + + I comandi qpush e qpop vi permettono di estrarre e inserire più patch alla volta. Potete identificare la patch di destinazione a cui volete giungere. Quando usate qpush specificando una destinazione, il comando inserirà patch fino a quando quella patch non si troverà in cima alla pila delle patch applicate. Quando usate qpop con una destinazione, MQ estrarrà patch fino a quando la patch di destinazione non si troverà in cima. + + Potete identificare una patch di destinazione usando il nome della patch oppure un numero. Se usate un identificatore numerico, il conteggio delle patch parte da zero; questo significa che la prima patch corrisponde a zero, la seconda a uno, e così via. + + + + Aggiornare le vostre patch quando il codice sottostante cambia + + Capita spesso di avere una pila di patch su un repository sottostante che non modificate direttamente. Se state lavorando sui cambiamenti a codice di terze parti, o su una funzione che impiegate più tempo a sviluppare rispetto alla velocità di cambiamento del codice al di sotto, avrete spesso bisogno di sincronizzarvi con il codice sottostante e di correggere ogni blocco delle vostre patch che non è più applicabile. Questa operazione si chiama rifondare la vostra serie di patch. + + Il modo più semplice per fare questo è di usare hg qpop per estrarre le vostre patch, poi invocare hg pull per propagare i cambiamenti nel repository sottostante e infine eseguire hg qpush per inserire nuovamente le vostre patch. MQ interromperà l'inserimento ogni volta che incontra una patch che non riesce ad applicare a causa di qualche conflitto, dandovi la possibilità di risolvere i conflitti, aggiornare la patch affetta tramite qrefresh e continuare a inserire fino a quando non avete corretto l'intera pila. + + Questo approccio è semplice e funziona bene se non vi aspettate modifiche al codice sottostante che influenzino quanto bene si applicano le vostre patch. Se la vostra pila di patch tocca codice che viene modificato in maniera frequente o invasiva nel repository sottostante, tuttavia, corregere a mano i blocchi rifiutati diventa velocemente una seccatura. + + È possibile automatizzare parzialmente il processo di rifondazione. Se le vostre patch si applicano in maniera pulita su una qualche revisione del repository sottostante, MQ può usare questa informazione per aiutarvi a risolvere i conflitti tra le vostre patch e revisioni differenti. + + Il processo è leggermente complicato. + + Per cominciare, invocate hg qpush -a per inserire tutte le vostre patch sulla revisione su cui sapete che si applicano in maniera pulita. + + Salvate una copia di backup della vostra directory delle patch usando hg qsave . Questo stampa il nome della directory in cui sono state salvate le patch. Salverà le patch in una directory chiamata .hg/patches.N, dove N è un piccolo intero. Inserirà anche un changeset di salvataggio in cima alle vostre patch applicate, per registrare internamente gli stati dei file series e status. + + Usate hg pull per propagare i nuovi cambiamenti nel repository sottostante. (Non invocate hg pull -u; vedete più avanti perché.) + + Aggiornate la directory di lavoro alla nuova revisione di punta, usando hg update per #override# le patch che avete inserito. + + Unite tutte le patch usando hg qpush -m -a. L'opzione di qpush dice a MQ di effettuare un'unione a tre vie se la patch non si riesce ad applicare. + + + Durante l'esecuzione di hg qpush , ogni patch nel file series viene applicata normalmente. Se una patch viene applicata con un fattore di incertezza o viene rifiutata, MQ guarda alla coda che avete salvato tramite qsave ed effettua un'unione a tre vie con il corrispondente changeset. Questa unione usa il normale meccanismo di unione di Mercurial, quindi potrebbe aprire uno strumento grafico per le unioni in modo da aiutarvi a risolvere i problemi. + + Quando avete finito di risolvere gli effetti di una patch, MQ aggiornerà la vostra patch sulla base dei risultati dell'unione. + + Alla fine di questo processo, il vostro repository avrà una testa aggiuntiva proveniente dalla vecchia coda delle patch, e una copia della vecchia coda delle patch si troverà in .hg/patches.N. Potete rimuovere la testa aggiuntiva usando hg qpop -a -n patches.N o hg strip. Potete cancellare .hg/patches.N una volta che siete sicuri che non ne avete più bisogno come backup. + + + + Identificare le patch + + I comandi MQ che lavorano con le patch vi permettono di fare riferimento a una patch usando il suo nome o un numero. Il riferimento per nome funziona in modo abbastanza ovvio: passate il nome foo.patch a qpush, per esempio, e il comando inserirà patch fino a quando foo.patch non verrà applicata. + + Potete abbreviare il riferimento a una patch usando sia un nome che una differenza numerica: foo.patch-2 significa due patch prima di foo.patch, mentre bar.patch+4 significa quattro patch dopo bar.patch. + + Il riferimento per indice non è molto differente. La prima patch visualizzata da qseries è la patch numero zero (sì, è uno di quei sistemi di conteggio che partono da zero), la seconda è la patch numero uno, e così via. + + MQ rende anche più facile lavorare con le patch quando state usando i normali comandi Mercurial. Tutti i comandi che accettano un identificatore di changeset accettano anche il nome di una patch applicata. MQ accresce le etichette normalmente presenti nel repository con un'etichetta eponima per ogni patch applicata. In più, le etichette speciali qbase e qtip identificano le patch applicate rispettivamente in fondo e in cima alla pila. + + Queste aggiunte alla funzione di #tagging# di Mercurial rendono ancora più facile utilizzare le patch. + + Volete bombardare di patch una mailing list con l'ultima serie dei vostri cambiamenti? + hg email qbase:qtip + (Non sapete cosa sia un bombardamento di patch? Leggete la .) + + Avete bisogno di vedere tutte le patch che da foo.patch in poi hanno toccato i file contenuti in una sottodirectory del vostro albero? + hg log -r foo.patch:qtip subdir + + + + Dato che MQ rende disponibili i nomi delle patch al resto di Mercurial tramite il meccanismo interno delle etichette, non avete bisogno di digitare l'intero nome di una patch quando volete identificarla per nome. + + Un'altra piacevole conseguenza del rappresentare i nomi di patch come etichette è che il comando hg log mostrerà il nome di una patch come un'etichetta, semplicemente come parte del proprio output normale. Questo rende facile distinguere visivamente le patch applicate dalle normali revisioni sottostanti. L'esempio seguente mostra alcuni comandi Mercurial in azione con le patch applicate. + + &interaction.mq.id.output; + + + + Informazioni utili + + Ci sono alcuni aspetti dell'uso di MQ che non trovano posto in sezioni dedicate, ma che è bene conoscere. Li presento qui, in un unico posto. + + + Normalmente, quando estraete una patch tramite qpop e poi la reinserite tramite qpush, il changeset che rappresenta la patch dopo l'estrazione/inserimento avrà una diversa identità rispetto al changeset che rappresentava l'hash in precedenza. Leggete FIXME per sapere perché succede questo. + + Non è una buona idea usare hg merge per unire i cambiamenti da un altro ramo con un changeset di patch, almeno se volete mantenere la natura di patch di quel changeset e dei changeset che si trovano sotto a quello nella pila delle patch. Se provate a farlo, sembrerà avere successo, ma l'effetto sarà quello di confondere MQ. + + + + + + Gestire le patch in un repository + + Dato che la directory .hg/patches di MQ risiede fuori dalla directory di lavoro di un repository Mercurial, il repository Mercurial sottostante non sa nulla della gestione o della presenza delle patch. + + Questo presenta l'interessante possibilità di gestire i contenuti della directory delle patch come un repository Mercurial indipendente. Questo può essere un modo utile per lavorare. Per esempio, potete lavorare su una patch per un po', aggiornala tramite qrefresh it, poi usare hg commit per registrare lo stato corrente della patch. Questo vi permette di ritornare a quella versione della patch più tardi. + + Potete quindi condividere differenti versioni della stessa pila di patch tra molteplici repository sottostanti. Uso questo quando sto sviluppando una funzione del kernel di Linux. Ho una copia intatta dei miei sorgenti del kernel per ogni diversa architettura di CPU e un repository clonato sotto ognuno di questi che contiene le patch su cui sto lavorando. Quando voglio collaudare un cambiamento su un'architettura differente, trasmetto le mie patch correnti al repository associato con il kernel di quell'architettura, estraggo e inserisco tutte le mie patch, e assemblo e collaudo quel kernel. + + Gestire le patch in un repository rende possibile a più sviluppatori di lavorare sulla stessa serie di patch senza scontrarsi tra loro, il tutto basandosi su sorgenti sottostanti che potrebbero o non potrebbero controllare. + + + Il supporto di MQ per i repository di patch + + MQ vi aiuta a lavorare con la directory .hg/patches come un repository; quando preparate un repository per lavorare con le patch usando qinit, potete passare l'opzione per creare la directory .hg/patches sotto forma di un repository Mercurial. + + + Se dimenticate di usare l'opzione , potete semplicemente posizionarvi nella directory .hg/patches in qualsiasi momento e invocare hg init. Non dimenticate, però, di aggiungere una voce per il file status al file .hgignore (hg qinit fa questo automaticamente per voi); non volete davvero gestire il file status. + + + Per convenienza, se MQ nota che la directory .hg/patches è un repository, userà automaticamente hg add per aggiungere ogni patch che create e importate. + + MQ fornisce il comando abbreviato qcommit che esegue hg commit nella directory .hg/patches, per salvare noiose digitazioni. + + Infine, come convenienza per gestire la directory delle patch, potete definire l'alias mq sui sistemi Unix. Per esempio, sui sistemi Linux che usano la shell bash, potete includere il seguente frammento nel vostro file ~/.bashrc. + + alias mq=`hg -R $(hg root)/.hg/patches' + + Potete poi invocare comandi della forma mq pull dal repository principale. + + + + Alcune cose a cui fare attenzione + + Il supporto di MQ per lavorare con un repository pieno di patch è limitato in alcuni aspetti di dettaglio. + + MQ non può automaticamente scoprire i cambiamenti che avete fatto alla directory delle patch. Se usate hg pull, fate delle modifiche a mano, o invocate hg update per aggiornare i cambiamenti alle patch o al file series, dovrete usare hg qpop e poi hg qpush nel repository sottostante per fare in modo che quei cambiamenti si mostrino anche là. Se dimenticate di fare questo, potete confondere l'idea che MQ si è fatto su quali patch sono state applicate. + + + + + Strumenti di terze parti che lavorano con le patch + + Una volta che avete lavorato che le patch per un po', vi troverete affamati di strumenti che vi aiutino a capire e manipolare le patch che state maneggiando. + + Il comando diffstat web:diffstat genera un istogramma delle modifiche effettuate a ogni file in una patch. Fornisce un buon modo di #get a sense of# una patch&emdash;quali file coinvolge e quante modifiche introduce a ogni file e nell'insieme. (Trovo che è una buona idea usare l'opzione di diffstat #as a matter of course#, in quanto altrimenti proverà a fare cose furbe con i prefissi dei nomi di file che almeno io trovo inevitabilmente confuse.) + +&interaction.mq.tools.tools; + + Il pacchetto patchutils web:patchutils è inestimabile. Fornisce un insieme di piccole utilità che seguono la filosofia Unix: ognuna fa una singola cosa utile con una patch. Il comando di patchutils che uso di più è filterdiff, che estrae sottinsiemi di un file di patch. Per esempio, data una patch che modifica centinaia di file attraverso dozzine di directory, una singola invocazione di filterdiff può generare una patch più piccola che tocca solo i file il cui nome corrisponde a un particolare pattern di tipo glob. Leggete la FIXME per un altro esempio. + + + + Strategie valide per lavorare con le patch + + Sia che voi stiate lavorando su una serie di patch da sottoporre a un progetto software libero o open source, o su una serie che intendete trattare come una sequenza di normali changeset una volta che avete finito, potete usare alcune semplici tecniche per mantenere bene organizzato il vostro lavoro. + + Date nomi descrittivi alle vostre patch. Un buon nome per una patch potrebbe essere rework-device-alloc.patch, perché vi suggerirà immediatamente qual è lo scopo della patch. I nomi lunghi non dovrebbero essere un problema; non digiterete i nomi spesso, ma invocherete comandi come qapplied e qtop più e più volte. Una buona denominazione diventa particolarmente importante quando avete un certo numero di patch con cui lavorare, o se vi state destreggiando tra un certo numero di attività differenti e le vostre patch ottengono solo una frazione della vostra attenzione. + + Fate attenzione alle patch su cui state lavorando. Usate frequentemente il comando qtop e date un'occhiata al testo delle vostre patch&emdash;per esempio, usando hg tip )&emdash;per assicurarvi di sapere dove vi trovate. Mi è capitato spesso di modificare e aggiornare una patch diversa da quella che intendevo, ed è spesso complicato trasferire le modifiche nella patch giusta dopo averle effettuate in quella sbagliata. + + Per questo motivo, vale davvero la pena di investire un po' di tempo per imparare a usare alcuni degli strumenti di terze parti che ho descritto nella , in particolare diffstat e filterdiff. Il primo vi darà velocemente un'idea di quali modifiche effettuerà la vostra patch, mentre il secondo vi renderà più facile #splice out# blocchi particolari di una patch e inserirli in un'altra. + + + + MQ #cookbook# + + + Gestire patch <quote>elementari</quote> + + Dato che il costo di aggiungere file in un nuovo repository Mercurial è così basso, ha molto senso gestire le patch in questo modo anche se volete semplicemente fare alcune modifiche a un archivo di sorgenti che avete scaricato. + + Cominciate con lo scaricare ed estrarre l'archivio dei sorgenti, trasformandoli in un repository Mercurial. + + &interaction.mq.tarball.download; + + Continuate creando una pila di patch e facendo le vostre modifiche. + + &interaction.mq.tarball.qinit; + + Diciamo che trascorrono alcune settimane o mesi e gli autori di quel pacchetto rilascia una nuova versione. Prima di tutto, propagate i loro cambiamenti nel repository. + + &interaction.mq.tarball.newsource; + + La serie di comandi iniziata con hg locate che avete appena invocato cancella tutti i file dalla directory di lavoro, in modo che l'opzione di hg commit possa effettivamente dirvi quali file sono stati davvero rimossi nella nuova versione dei sorgenti. + + Infine, potete applicare le vostre patch al nuovo albero. + + &interaction.mq.tarball.repush; + + + + Combinare intere patch + + MQ vi fornisce il comando qfold per consentirvi di combinare intere patch. Questo comando include le patch che nominate, nell'ordine in cui le nominate, nella patch applicata in cima alla pila, e concatena le loro descrizioni alla fine della descrizione di questa patch. Le patch che includete non devono essere applicate prima di includerle. + + L'ordine in cui includete le patch è importante. Se la vostra patch applicata in cima alla pila è foo e voi utilizzate qfold per includere bar e quux in essa, otterrete una patch che opererà come se aveste applicato prima foo, poi bar, seguito da quux. + + + + Unire parte di una patch con un'altra + + Unire parte di una patch con un'altra patch è più difficile che combinare intere patch. + + Se volete spostare cambiamenti a interi file, potete usare le opzioni e di filterdiff per scegliere le modifiche da ritagliare da una patch, aggiungendo il risultato del comando in coda alla patch con cui volete effettuare l'unione. Di solito non avrete bisogno di modificare la patch da cui prelevate le modifiche da unire. Piuttosto, MQ riporterà alcune parti rifiutate quando eseguite qpush su di essa (a causa dei blocchi che avete spostato nell'altra patch) e voi potrete semplicemente aggiornare la patch tramite qrefresh per scartare i blocchi duplicati. + + Se avete una patch con più blocchi che modificano un file, e volete spostare solo alcuni di questi blocchi, il lavoro diventa più complicato, ma potete comunque automatizzarlo parzialmente. Usate lsdiff -nvv per stampare alcuni metadati sulla patch. + + &interaction.mq.tools.lsdiff; + + Questo comando stampa tre tipi diversi di numeri: + + (nella prima colonna) un numero di file per identificare ogni file modificato dalla patch; + + (sulla riga seguente, indentato) il numero di riga del file modificato dove comincia il blocco; e + + (sulla stessa riga) un numero di blocco per identificare quel blocco. + + + + Dovrete ispezionare visivamente e leggere la patch per identificare i numeri di file e di blocco che volete, ma poi potrete passarli alle opzioni e di filterdiff per selezionare esattamente quel file e quel blocco che volete estrarre. + + Una volta che avete questo blocco, potete aggiungerlo in coda alla vostra patch di destinazione e continuare con il resto della . + + + + + Differenze tra quilt e MQ + + Se avete già familiarità con quilt, MQ fornisce un insieme di comandi simile. Ci sono alcune differenze nel modo in cui questi comandi lavorano. + + Avrete già notato che la maggior parte dei comandi di quilt hanno una controparte MQ che comincia semplicemente con una q. Le eccezioni sono i comandi add e remove di quilt, le cui controparti sono i normali comandi Mercurial hg add e hg remove. Non c'è alcun comando MQ equivalente al comando quilt edit. + + +