# HG changeset patch # User Giulio@puck # Date 1250165231 -7200 # Node ID 4ce1732180967c937f527fda1e4aba27dae3914d # Parent 80e5a41352d73a749ef17311d85ddf627c52b975 Deep revision of Ch.13. diff -r 80e5a41352d7 -r 4ce173218096 it/ch13-mq-collab.xml --- a/it/ch13-mq-collab.xml Wed Aug 12 23:47:34 2009 +0200 +++ b/it/ch13-mq-collab.xml Thu Aug 13 14:07:11 2009 +0200 @@ -2,54 +2,54 @@ Usi avanzati di Mercurial Queues - Mentre è facile imparare gli usi più semplici di Mercurial Queues, l'uso di un po' di disciplina e di alcune delle capacità di MQ usate meno frequentemente rende possibile lavorare in ambienti di sviluppo complicati. - - In questo capitolo, userò come esempio una tecnica che ho usato per gestire lo sviluppo di un driver del kernel di Linux per un dispositivo Infiniband. Il driver in questione è grande (almeno per le dimensioni dei driver), con 25.000 righe di codice sparse su 35 file sorgente. Viene mantenuto da un piccolo gruppo di sviluppatori. - - Sebbene la maggior parte del materiale in questo capitolo sia specifica per Linux, gli stessi principi si applicano per qualsiasi base di codice di cui non siate i proprietari principali e su cui dobbiate fare un bel po' di lavoro. + Sebbene sia facile imparare gli usi più semplici di Mercurial Queues, sono un pizzico di disciplina e alcune delle funzioni meno usate di MQ che rendono possibile lavorare in ambienti di sviluppo complicati. + + In questo capitolo, userò come esempio una tecnica che ho impiegato per gestire lo sviluppo di un driver del kernel di Linux per un dispositivo Infiniband. Il driver in questione è grande (almeno per le dimensioni dei driver), poiché contiene 25.000 righe di codice distribuite su 35 file sorgente. Viene mantenuto da un piccolo gruppo di sviluppatori. + + Sebbene la maggior parte del materiale in questo capitolo sia specifica per Linux, gli stessi principi si applicano per qualsiasi base di codice di cui non siate i proprietari principali e su cui dobbiate fare parecchio lavoro. Il problema dei molti obiettivi - Il kernel di Linux cambia rapidamente e non è mai stato stabile internamente, in quanto gli sviluppatori effettuano frequentemente modifiche drastiche tra una release e l'altra. Questo significia che una versione del driver che funziona bene con una particolare release del kernel non sarà nemmeno in grado di compilare correttamente su, tipicamente, qualsiasi altra versione. - - Per mantenere un driver, dobbiamo tenere a mente un certo numero di versioni di Linux differenti. - - Un obiettivo è l'albero di sviluppo principale del kernel di Linux. In questo caso, il mantenimento del codice è parzialmente condiviso con altri sviluppatori della comunità del kernel, che effettuano modifiche #drive-by# al driver man mano che sviluppano e rifiniscono i sottosistemi del kernel. - - Manteniamo anche un certo numero di #backports# verso vecchie versioni del kernel di Linux, per supportare le necessità di cienti che stanno utilizzando distribuzioni di Linux più vechie che non incorporano i nostri driver. (Effetuare il #backport# del codice significa modificarlo per farlo funzionare in una versione del suo ambiente obiettivo più vecchia di quella per la quale era stato sviluppato.) - - Infine, rilasciamo il software seguendo un'agenda che non è necessariamente allineata con quella usata dagli sviluppatori del kernel e dai distributori di Linux, in modo che possiamo consegnare nuove funzionalità ai cienti senza obbligarli ad aggiornare il loro kernel o la loro intera distribuzione. + Il kernel di Linux cambia rapidamente e non è mai stato stabile internamente, in quanto gli sviluppatori effettuano frequentemente modifiche drastiche tra una release e l'altra. Questo significa che, di solito, una versione del driver che funziona bene con una particolare versione rilasciata del kernel non potrà nemmeno essere compilata correttamente su qualsiasi altra versione del kernel. + + Per mantenere un driver, dobbiamo considerare un certo numero di versioni di Linux differenti. + + Un obiettivo è l'albero di sviluppo principale del kernel di Linux. In questo caso, le attività di manutenzione del codice sono parzialmente condivise con altri sviluppatori della comunità del kernel, che effettuano modifiche estemporanee al driver man mano che sviluppano e rifiniscono i sottosistemi del kernel. + + Manteniamo anche un certo numero di backport (letteralmente, conversioni all'indietro) verso vecchie versioni del kernel di Linux, per supportare le necessità di clienti che stanno utilizzando distribuzioni di Linux più vecchie che non incorporano i nostri driver. (Effetuare il backport del codice significa modificarlo per farlo funzionare in una versione del suo ambiente di destinazione più vecchia di quella per la quale era stato sviluppato.) + + Infine, rilasciamo il software seguendo una tabella di marcia che non è necessariamente allineata con quella usata da chi sviluppa il kernel e da chi distribuisce il sistema operativo, in modo da poter consegnare nuove funzionalità ai clienti senza obbligarli ad aggiornare il loro kernel o la loro intera distribuzione. Approcci seducenti che non funzionano bene - Ci sono due modi standard per mantenere un software che ha come obiettivo molti ambienti diversi. - - Il primo è mantenere un certo numero di rami, ognuno designato per un singolo obiettivo. Il problema di questo approccio è che dovete mantenere una ferrea disciplina nel flusso dei cambiamenti tra i repository. Una nuova funzione o correzione di bug deve prendere vita in un repository intatto, poi passare a tutti i repository #backport#. Le modifiche di #backport# sono più limitate nei rami in cui dovrebbero propagarsi; una modifica di #backport# che viene applicata a un ramo a cui non appartiene probabilmente impedirebbe al driver di compilare. - - Il secondo è quello di mantenere un singolo albero di sorgenti pieno di istruzioni condizionali che attivano o disattivano pezzi di codice a seconda dell'obiettivo designato. Dato che queste istruzioni ifdefs non sono permesse nell'albero del kernel di Linux, un processo manuale o automatico deve essere seguito per eliminarli e produrre un albero pulito. Una base di codice mantenuta in questo modo diventa rapidamente un macello di blocchi condizionali che sono difficili da capire e mantenere. - - Nessuno di questi approcci è particolarmente adatto a situaizoni in cui non possedete la copia canonica di un albero di sorgenti. Nel caso di un driver Linux che viene distribuito insieme al kernel stadard, l'albero di Linus contiene la copia del codice che il mondo tratterà come canonica. La versione a monte del mio driver può essere modificata da persone che non conosco, senza che io nemmeno lo scopra fino a quando i cambiamenti non appaiono nell'albero di Linus. - - Questi approcci hanno la debolezza aggiunta di rendere difficile generare patch ben formate da sottoporre a monte. - - In linea di principio, Mercurial Queues sembra un buon candidato per gestire uno scenario di sviluppo come quello qui delineato. Sebbene questo sia indubbiamente il caso, MQ contiene anche alcune funzioni aggiuntive che rendono il lavoro più piacevole. + Ci sono due modi standard per mantenere un software che deve funzionare in molti ambienti diversi. + + Il primo è mantenere un certo numero di rami, ognuno dedicato a un singolo ambiente. Il problema di questo approccio è che dovete mantenere una ferrea disciplina nel flusso dei cambiamenti tra i repository. Una nuova funzione o correzione di bug deve prendere vita in un repository intatto, poi passare a tutti i repository di backport. Le modifiche di backport dovrebbero propagarsi verso un numero di rami più limitato, in quanto l'applicazione di una modifica di backport su un ramo a cui non appartiene probabilmente impedirà al driver di essere compilato. + + Il secondo è quello di mantenere un singolo albero di sorgenti pieno di istruzioni condizionali che attivano o disattivano parti di codice a seconda dell'ambiente designato. Dato che queste istruzioni ifdef non sono permesse nell'albero del kernel di Linux, è necessario seguire una procedura manuale o automatica per eliminarle e produrre un albero pulito. Una base di codice mantenuta in questo modo diventa rapidamente un caos di blocchi condizionali che sono difficili da capire e mantenere. + + Nessuno di questi approcci è particolarmente adatto a situazioni in cui non possedete la copia ufficiale di un albero di sorgenti. Nel caso di un driver Linux che viene distribuito insieme al kernel standard, l'albero di Linus contiene la copia del codice che verrà considerata ufficiale dal resto del mondo. La versione a monte del mio driver può essere modificata da persone che non conosco, senza che io nemmeno lo scopra fino a quando i cambiamenti non appaiono nell'albero di Linus. + + Questi approcci hanno la debolezza aggiuntiva di rendere difficile generare patch ben formate da presentare a monte. + + In linea di principio, Mercurial Queues sembra un buon candidato per gestire uno scenario di sviluppo come quello qui delineato. Non solo questo è indubbiamente il caso, ma MQ contiene anche alcune funzioni aggiuntive che rendono il lavoro più piacevole. Applicare patch in maniera condizionata con le guardie - Forse il miglior modo per mantenere la sanità con così tanti obiettivi è quello di essere in grado di scegliere patch specifiche da applicare a una data situazione. MQ fornisce una funzione chiamata guardie (originata dal comando guards di quilt) che fa proprio questo. Per cominciare, creiamo un semplice repository per fare qualche esperimento. + Forse il miglior modo per non impazzire con così tanti ambienti obiettivo è quello di essere in grado di scegliere patch specifiche da applicare a una data situazione. MQ fornisce una funzione chiamata guardie (originata dal comando guards di quilt) che fa proprio questo. Per cominciare, creiamo un semplice repository per fare qualche esperimento. &interaction.mq.guards.init; - Questo ci dà un piccolo repository che contiene due patch che non hanno alcuna dipendenza reciproca, perché toccano file differenti. - - L'idea dietro all'applicazione condizionale è che potete etichettare una patch con una guardia, che è semplicemente una stringa di testo di vostra scelta, poi dire a MQ di selezionare le guardie specifiche da usare al momento di applicare le patch. MQ applicherà, o salterà, una patch con guardia, a seconda delle guardie che avete selezionato. + Questo ci fornisce un piccolo repository contenente due patch che non hanno alcuna dipendenza reciproca, perché coinvolgono file differenti. + + L'idea alla base dell'applicazione condizionale è la possibilità di etichettare una patch con una guardia, che è semplicemente una stringa di testo di vostra scelta, per poi dire a MQ di selezionare le guardie specifiche da usare al momento di applicare le patch. MQ applicherà, o salterà, una patch con guardia, a seconda delle guardie che avete selezionato. Una patch può avere un numero arbitrario di guardie, ognuna delle quali è positiva (applica questa patch se questa guardia è selezionata) o negativa (salta questa patch se questa guardia è selezionata). Una patch senza alcuna guardia viene sempre applicata. @@ -57,27 +57,27 @@ Controllare le guardie su una patch - Il comando qguard vi permette di determinare quali guardie dovrebbero applicarsi a una patch, o di visualizzare le guardie che sono già in effetto. Senza alcun argomento, il comando mosra le guardie della patch correntemente in cima alla pila. - - &interaction.mq.guards.qguard; + Il comando qguard vi permette di determinare quali guardie dovrebbero applicarsi a una patch, o di visualizzare le guardie già in effetto. Senza alcun argomento, il comando mostra le guardie della patch attualmente in cima alla pila. + + &interaction.mq.guards.qguard; Per impostare una guardia positiva su una patch, fate precedere il nome della guardia da un +. - &interaction.mq.guards.qguard.pos; + &interaction.mq.guards.qguard.pos; Per impostare una guardia negativa, fate precedere il nome della guardia da un -. &interaction.mq.guards.qguard.neg; - Notate che in questo caso gli argomenti del comando hg qguard sono introdotti con il prefisso --, così Mercurial non interpreterà il testo -quux come un'opzione. + Notate che in questo caso gli argomenti del comando hg qguard sono introdotti con il prefisso --, così Mercurial eviterà di interpretare il testo -quux come un'opzione. Impostare e modificare - Il comando qguard imposta le guardie su una patch, ma non le modifica. Questo significa che se invocate hg qguard +a +b su una patch, quindi invocate hg qguard +c sulla stessa patch, l'unica guardia che verrà impostata successivamente sarà +c. + Il comando qguard imposta le guardie su una patch, ma non le modifica. Questo significa che se invocate hg qguard +a +b su una patch, quindi invocate hg qguard +c sulla stessa patch, l'unica guardia che risulterà successivamente impostata sarà +c. - Mercurial memorizza le guardie nel file series, in una forma facile sia da capire che da modificare a mano. (In altre parole, non dovete usare il comando qguard se non volete, perché potete tranquillamente modificare direttamente il file series file.) + Mercurial memorizza le guardie nel file series, in una forma facile sia da capire che da modificare a mano. (In altre parole, non dovete usare il comando qguard se non volete, perché potete semplicemente modificare il file series.) &interaction.mq.guards.series; @@ -87,9 +87,9 @@ Il comando qselect determina quali guardie sono attive in un dato momento. Il suo effetto è quello di determinare quali patch verranno applicate da MQ la prossima volta che invocherete qpush. Non ha alcun altro effetto, in particolare non agisce in alcun modo sulle patch che sono già applicate. - Invocato senza argomenti, il comando qselect elenca le guardie attualmente in effetto, una per ogni riga di output. Ogni argomento viene trattato come il nome di una guardia da applicare. - - &interaction.mq.guards.qselect.foo; + Invocato senza argomenti, il comando qselect elenca le guardie attualmente in effetto, una per ogni riga. Gli argomenti vengono trattati come i nomi delle guardie da applicare. + + &interaction.mq.guards.qselect.foo; Nel caso siate interessati, le guardie attualmente selezionate vengono memorizzate nel file guards. @@ -117,7 +117,7 @@ Le regole usate da MQ per decidere se applicare una patch sono le seguenti. - Una patch che non ha guardie è sempre applicata. + Una patch che non ha guardie viene sempre applicata. Se la patch ha una qualsiasi guardia negativa che corrisponde a una qualsiasi guardia correntemente selezionata, la patch viene saltata. @@ -129,13 +129,13 @@ - Riordinare l'ambiente di lavoro - - Lavorando sul driver di dispositivo menzionato in precedenza, non applico le patch a un normale albero del kernel di Linux. invece, uso un repository che contiene solo una fotografia dei file sorgente che sono rilevanti per lo sviluppo del dispositivo Infiniband. Le dimensioni di questo repository sono l'1% delle dimensioni di un repository del kernel, quindi è più facile lavorare con esso. - - Quindi scelgo una versione di base sulla quale le patch vengono applicate. Questa è la fotografia dell'albero del kernel di Linux a una revisione di mia scelta. Quando scatto la fotografia, registro l'identificatore di changeset dal repository del kernel nel messaggio di commit. Dato che la fotografia mantiene la forma e il contenuto delle parti rilevanti dell'albero del kernel, posso applicare le mie patch sul mio repository ridotto o sul un normale albero del kernel. - - Normalmente, l'albero di base a cui applicare le patch dovrebbe essere una fotografia di un albero a monte molto recente. Questo facilita al meglio lo sviluppo di patch che possono essere facilmente sottoposte a monte con poche o addirittura nessuna modifica. + Ridimensionare l'ambiente di lavoro + + Lavorando sul driver di dispositivo menzionato in precedenza, non applico le patch a un normale albero del kernel di Linux, ma uso un repository che contiene solo una fotografia dei file sorgente rilevanti per lo sviluppo del dispositivo Infiniband. È più facile lavorare con questo repository, perché le sue dimensioni sono l'1% delle dimensioni di un repository del kernel. + + Poi scelgo una versione di base sulla quale le patch vengono applicate. Questa è una fotografia dell'albero del kernel di Linux scattata su una revisione di mia scelta. Quando scatto la fotografia, registro l'identificatore di changeset proveniente dal repository del kernel nel messaggio di commit. Dato che la fotografia mantiene la forma e il contenuto delle parti rilevanti dell'albero del kernel, posso applicare le mie patch sul mio repository ridotto o su un normale albero del kernel. + + Normalmente, l'albero di base a cui applicare le patch dovrebbe essere una fotografia di un albero a monte molto recente. Questa è la condizione migliore per facilitare lo sviluppo di patch che possono essere presentate a monte con poche o addirittura nessuna modifica. @@ -145,25 +145,25 @@ La sequenza dei gruppi di patch che mantengo è la seguente. L'ordine di questi gruppi è importante per i motivi che verranno descritti dopo aver introdotto i gruppi. - Il gruppo delle patch accettate. Sono le patch che il gruppo di sviluppo ha sottoposto al mantenitore del sottosistema Infiniband e che sono state accettate, ma che non sono presenti nella fotografia su cui il repository ridotto è basato. Queste sono patch a sola lettura, presenti solo allo scopo di trasformare l'albero in uno stato simile a quello in cui si trova nel repository del mantenitore a monte. - - Il gruppo delle patch da rielaborare. Sono le patch che ho sottoposto ma per le quali il mantenitore a monte ha richiesto alcune modifiche prima di poterle accettare. - - Il gruppo delle patch in sospeso. Sono le patch che non ho ancora sottoposto al mantenitore a monte, ma su cui dobbiamo finire di lavorare. Queste patch saranno a sola lettura per un po'. Se il mantenitore a monte le accetta al momento di sottoporle, le sposterò alla fine del gruppo accettate. Se ne richiede la modifica, le sposterò all'inizio del gruppo da rielaborare. - - Il gruppo delle patch in corso. Sonon le patch che vengono attivamente sviluppate e che non dovrebbero essere ancora sottoposte a nessuno. - - Il gruppo delle patch #backport#. Sono le patch che riadattano l'albero dei sorgenti a una vecchia versione dell'albero del kernel. - - Il gruppo delle patch da non rilasciare. Sono le patch che per qualche ragione non dovrebbero mai essere sottoposte a monte. Per esempio, una patch di questo tipo potrebbe modificare le stringhe di identificazione incluse nei driver per rendere più facile distinguere, nel campo, tra una versione del driver presa dall'albero e una versione consegnata a un venditore di distribuzioni. - - - - Tornando alle ragioni per cui i gruppi di patch sono ordinati in questo modo, ci piacerebbe che le patch più in basso nella pila siano stabili il più possibile, in modo da non aver bisogno di rielaborare le patch più in alto a causa di modifiche a loro contesto. Mettere le patch che non verranno mai cambiate all'inizio del file series serve proprio a questo scopo. - - Ci piacerebbe anche applicare le patch che sappiamo di aver bisogno di modificare in cima a un albero di sorgenti che somiglia il più possibile all'albero a monte. Questo è il motivo per cui teniamo le patch accettate in giro per un po'. - - Le patch dei gruppi #backport# e non rilasciare si trovano alla fine del file series. Le patch #backport# devono essere applicate su tutte le altre patch e anche le patch da non rilasciare potrebbero starsene al sicuro. + Il gruppo delle patch accettate. Sono le patch che il gruppo di sviluppo ha proposto al mantenitore del sottosistema Infiniband e che sono state accettate, ma che non sono presenti nella fotografia su cui si basa il repository ridotto. Queste sono patch a sola lettura, presenti unicamente allo scopo di trasformare l'albero in uno stato simile a quello in cui si trova nel repository del mantenitore a monte. + + Il gruppo delle patch da rielaborare. Sono le patch che ho proposto ma per le quali il mantenitore a monte ha richiesto alcune modifiche prima di poterle accettare. + + Il gruppo delle patch in sospeso. Sono le patch che non ho ancora proposto al mantenitore a monte, ma su cui dobbiamo finire di lavorare. Queste patch saranno a sola lettura per un po'. Se il mantenitore a monte le accetta al momento di proporle, le sposterò alla fine del gruppo accettate. Se ne richiede la modifica, le sposterò all'inizio del gruppo da rielaborare. + + Il gruppo delle patch in corso. Sono le patch che vengono attivamente sviluppate e che non dovrebbero essere ancora presentate a nessuno. + + Il gruppo delle patch di backport. Sono le patch che riadattano l'albero dei sorgenti a una vecchia versione dell'albero del kernel. + + Il gruppo delle patch da non rilasciare. Sono le patch che per qualche ragione non dovrebbero mai essere presentate a monte. Per esempio, una patch di questo tipo potrebbe modificare le stringhe di identificazione incluse nei driver per rendere più facile distinguere, nel testo del campo, tra una versione del driver presa dall'albero e una versione consegnata a un rivenditore di distribuzioni. + + + + Tornando alle ragioni per cui i gruppi di patch sono ordinati in questo modo, ci piacerebbe che le patch più in basso nella pila siano il più possibile stabili, in modo da non aver bisogno di rielaborare le patch più in alto a causa di modifiche a loro contesto. Mettere le patch che non verranno mai cambiate all'inizio del file series serve proprio a questo scopo. + + Ci piacerebbe anche applicare le patch che sappiamo di aver bisogno di modificare su un albero di sorgenti che somiglia il più possibile all'albero a monte. Questo è il motivo per cui teniamo le patch accettate in giro per un po'. + + Le patch di backport e da non rilasciare si trovano alla fine del file series. Le patch di backport devono essere applicate su tutte le altre patch, e le patch da non rilasciare potrebbero anche rimanere in un posto sicuro. @@ -174,25 +174,25 @@ Le patch accettate sono sorvegliate da accepted. Questa guardia è abilitata per la maggior parte del tempo. Quando sto applicando le patch su un albero dove le patch sono già presenti, posso disabilitare questa patch così le patch che la seguono verranno applicate in maniera pulita. - Le patch che sono terminate, ma non ancora sottoposte, non hanno guardie. Se sto applicando la pila delle patch a una copia dell'albero a monte, non ho bisogno di abilitare alcuna guardia per ottenere un albero di sorgenti ragionevolmente sicuro. - - Le patch che hanno bisogno di essere rielaborate prima di venire nuovamente sottoposte sono sorvegliate da rework. - - Per quelle patch che sono ancora sotto sviluppo, uso devel. - - Una patch #backport# potrebbe avere diverse guardie, una per ogni versione del kernel a cui si applica. Per esempio, una patch che #backports# una parte del codice alla versione 2.6.9 del kernel avrà una guardia 2.6.9 guard. - - - Questa varietà di guardie mi concede una flessibilità considerevole nel determinare quale tipo di albero dei sorgenti creare. Per la maggior parte delle situazioni, la selezione delle guardie appropriate viene automatizzata durante il processo di assemblaggio, ma posso regolare manualmente le guardie da usare nelle circostanze meno comuni. + Le patch che sono terminate, ma non sono ancora state proposte, non hanno guardie. Se sto applicando la pila delle patch a una copia dell'albero a monte, non ho bisogno di abilitare alcuna guardia per ottenere un albero di sorgenti ragionevolmente sicuro. + + Le patch che hanno bisogno di essere rielaborate prima di venire nuovamente presentate sono sorvegliate da rework. + + Per quelle patch che sono ancora in lavorazione, uso devel. + + Una patch di backport potrebbe avere diverse guardie, una per ogni versione del kernel a cui si applica. Per esempio, una patch che effettua il backport di una parte del codice alla versione 2.6.9 del kernel avrà una guardia 2.6.9. + + + Questa varietà di guardie mi concede una flessibilità considerevole nel determinare quale tipo di albero dei sorgenti creare. Nella maggior parte delle situazioni, la selezione delle guardie appropriate viene automatizzata durante il processo di assemblaggio, ma posso regolare manualmente le guardie da usare nelle circostanze meno comuni. - L'arte di scrivere patch #backport# - - Usando MQ, scrivere una patch #backport# è un processo semplice. Tutto quello che una patch di questo tipo deve fare è modificare una parte di codice che usa una funzione del kernel non presente in una vecchia versione del kernel, in modo che il driver continui a funzionare correttamente con la vecchia versione. - - Un obiettivo utile da raggiungere nella scrittura di una buona patch #backport# è far sembrare il codice come se fosse stato scritto per la vecchia versione del kernel che state considerando. Meno intrusiva è la patch, più facile sarà da capire e mantenere. Se state scrivendo una collezione di patch #backport# per evitare l'effetto macello causato da molti #ifdef (blocchi di codice che vengono usati solo in maniera condizionata) nel vostro codice, evitate di introdurre nelle patch #ifdef dipendenti dalle versioni del kernel. Piuttosto, scrivete diverse patch, ognuna delle quali provoca cambiamenti incondizionati, e controllate la loro applicazione usando le guardie. - - Ci sono due ragioni per separare le patch #backport# in un gruppo distinto dalle patch normali i cui effetti vengono modificati da quelle. Il primo è che mescolarle insieme rende più difficile usare uno strumento come l'estensione patchbomb per automatizzare il processo di sottoposizione delle patch a un mantenitore a monte. Il secondo è che le patch #backport# potrebbero perturbare il contesto in cui una successiva patch normale viene applicata, rendendo impossibile applicare la patch normale in maniera pulita senza che la patch #backport# precedente sia già stata applicata. + L'arte di scrivere patch di backport + + Usando MQ, scrivere una patch di backport è un processo semplice. Tutto quello che una patch di questo tipo deve fare è modificare una parte di codice che usa una funzione del kernel non presente in una vecchia versione del kernel, in modo che il driver continui a funzionare correttamente con la vecchia versione. + + Un obiettivo utile da raggiungere nella scrittura di una buona patch di backport è far sembrare che il codice sia stato scritto per la vecchia versione del kernel che state considerando. Meno intrusiva è la patch, più facile sarà da capire e mantenere. Se state scrivendo una collezione di patch di backport per evitare l'effetto caos causato da molte istruzioni #ifdef (contenenti blocchi di codice che vengono usati solo in maniera condizionata) nel vostro codice, evitate di introdurre nelle patch #ifdef dipendenti dalle versioni del kernel. Piuttosto, scrivete diverse patch, ognuna delle quali provoca cambiamenti incondizionati, e controllate la loro applicazione usando le guardie. + + Ci sono due ragioni per separare le patch di backport in un gruppo distinto dalle patch normali i cui effetti vengono modificati da quelle. Il primo è che mescolarle insieme rende più difficile usare uno strumento come l'estensione patchbomb per automatizzare il processo di spedizione delle patch a un mantenitore a monte. Il secondo è che le patch di backport potrebbero perturbare il contesto in cui una successiva patch normale viene applicata, rendendo impossibile applicare la patch normale in maniera pulita senza che la patch di backport precedente sia già stata applicata. @@ -202,36 +202,36 @@ Organizzare le patch in directory - Se state lavorando su un progetto sostanzioso con MQ, non è difficile accumulare un grande numero di patch. Per esempio, mi è capitato di avere un repository di patch contenente più di 250 patch. - - Se potete raggruppare queste patch in categorie logiche separate, potete memorizzarle in directory differenti, in quanto MQ non ha problemi a usare nomi di patch che contengono separatori di percorso. + Se state lavorando su un progetto considerevole con MQ, non è difficile accumulare un grande numero di patch. Per esempio, mi è capitato di avere un repository di patch contenente più di 250 patch. + + Se riuscite a raggruppare queste patch in categorie logiche separate, potete memorizzarle in directory differenti, in quanto MQ non ha problemi a usare nomi di patch che contengono separatori di percorso. Visualizzare la cronologia di una patch - Se sviluppate un insieme di patch per un lungo periodo, è una buona idea mantenerle in un repository, come discusso nella . Se fate in questo modo, scoprirete velocemente che è impraticabile usare il comando hg diff per guardare la cronolgia dei cambiamenti di una patch. In parte, questo succede perché state guardando la derivata seconda del codice reale (un diff di un diff), ma anche perché MQ aggiunge rumore al processo modificando le marcature temporali e i nomi di directory quando aggiorna una patch. - - Tuttavia, potete usare l'estensione extdiff inclusa in Mercurial per trasformare il diff didue versioni di una patch in qualcosa di leggibile. Per fare questo, avrete bisogno di un pacchetto di terze parti chiamato patchutils web:patchutils. Questo fornisce un comando chiamato interdiff che mostra le differenze tra due diff di un diff. Usato su due versioni dello stesso diff, genra un diff che rappresenta le differenze tra la prima e la seconda versione. + Se sviluppate un insieme di patch per un lungo periodo, è una buona idea mantenerle in un repository, come discusso nella . Se fate in questo modo, scoprirete velocemente che è impraticabile usare il comando hg diff per esaminare la cronolgia dei cambiamenti di una patch. In parte, questo succede perché state osservando la derivata seconda del codice reale (un diff di un diff), ma anche perché MQ aggiunge rumore al processo modificando le marcature temporali e i nomi di directory quando aggiorna una patch. + + Tuttavia, potete usare l'estensione extdiff inclusa in Mercurial per rendere leggibile il diff di due versioni di una patch. Per fare questo, avrete bisogno di un pacchetto di terze parti chiamato patchutils web:patchutils. Il pacchetto fornisce un comando chiamato interdiff che mostra le differenze tra due diff di un diff. Usato su due versioni dello stesso diff, genera un diff che rappresenta le differenze tra la prima e la seconda versione. Potete abilitare l'estensione extdiff nel solito modo, aggiungendo una riga alla sezione extensions del vostro file ~/.hgrc. [extensions] extdiff = - Il comando interdiff si aspetta che gli vengano passati i nomi di due file, ma l'estensione extdiff passa una coppia di directory al programma che segue, ognuna delle quali può contenere un numero arbitrario di file. Quindi abbiamo bisogno di un piccolo programma che invochi interdiff su ogni coppia di file in quelle due directory. Questo programma è disponibile sotto il nome di hg-interdiff nella directory examples del repository di codice sorgente che accompagna questo libro. - - Con il programma hg-interdiff nel percorso di ricerca della vostra shell, potete eseguilo come segue, dall'interno di una directory di patch gestita da MQ: - hg extdiff -p hg-interdiff -r A:B my-change.patch + Il comando interdiff si aspetta che gli vengano passati i nomi di due file, ma l'estensione extdiff passa una coppia di directory al programma che invoca, ognuna delle quali può contenere un numero arbitrario di file. Quindi abbiamo bisogno di un piccolo programma che invochi interdiff su ogni coppia di file in quelle due directory. Questo programma è disponibile sotto il nome di hg-interdiff nella directory examples del repository di codice sorgente che accompagna questo libro.FIXME Where in the world is the file in that repository? + + Dopo aver incluso il programma hg-interdiff nel percorso di ricerca della vostra shell, potete eseguirlo come segue, dall'interno di una directory di patch gestita da MQ: + hg extdiff -p hg-interdiff -r A:B mio-cambiamento.patch Dato che vorrete usare questo comando prolisso molto spesso, potete fare in modo che hgext lo renda disponibile come un normale comando Mercurial, modificando ancora una volta il vostro file ~/.hgrc. [extdiff] cmd.interdiff = hg-interdiff - Questo istruisce hgext a rendere disponibile il comando interdiff, in modo che possiate accorciare l'invocazione precedente di extdiff a qualcosa di un po' più maneggevole. + Questa riga ordina a hgext di rendere disponibile il comando interdiff, in modo che possiate ridurre l'invocazione precedente di extdiff a qualcosa di un po' più maneggevole. hg interdiff -r A:B my-change.patch Il comando interdiff funziona bene solo se i file sottostanti dai quali vengono generate le versioni di una patch rimangono gli stessi. Se create una patch, modificate i file sottostanti e poi rigenerate la patch, interdiff potrebbe non produrre alcun risultato utile. - L'utilità dell'estensione extdiff va oltre il semplice miglioramento della presentazione delle patch gestite da MQ. Per avere ulteiori informazioni, leggete la . + L'utilità dell'estensione extdiff va oltre il semplice miglioramento della presentazione delle patch gestite da MQ. Per avere ulteriori informazioni, leggete la .