2010-09-15 14 views
17

Questa è una domanda sulle migliori pratiche e mi aspetto che la risposta sia "dipende". Spero solo di imparare più scenari e flussi di lavoro nel mondo reale.Come gestire lo sviluppo concorrente con mercurial?

Prima di tutto, sto parlando di diversi cambiamenti per lo stesso progetto, quindi nessun subrepo per favore.

Supponiamo di avere il codice base in un repository hg. Inizi a lavorare su una nuova caratteristica complicata A, poi un bug complicato B viene segnalato dal tuo tester di fiducia (hai i tester, giusto?).

E 'banale se (la correzione per) B dipende da A. È simlply CI a poi ci B.

La mia domanda è che cosa fare quando sono indipendenti (o almeno sembra ora).

mi vengono in mente dei seguenti modi:

  1. Utilizzare un clone separato per B.
  2. Utilizzare i rami anonimi o con nome, o segnalibri, nello stesso repository.
  3. Utilizzare MQ (con patch B in cima a A).
  4. Usa MQ ramificato (ti spiegherò più avanti).
  5. Uso MQ multipla (dal 1,6)

1 e 2 sono coperti da un excellent blog da @Steve Losh collegato da un slightly related question.

L'enorme vantaggio di 1 rispetto alle altre scelte è che non richiede alcuna ricostruzione quando si passa dal lavorare su una cosa all'altra, perché i file sono fisicamente separati e indipendenti. Quindi è davvero l'unica scelta se, ad esempio, A e/o B toccano un file di intestazione che definisce un booleano a tre stati ed è incluso in migliaia di file C (non dirmi che non hai visto un codice legacy simile base).

3 è probabilmente il più semplice (in termini di configurazione e sovraccarico), e è possibile capovolgere l'ordine di A e B se B è una correzione piccola e/o urgente. Tuttavia può diventare complicato se A e B toccano lo stesso file (s). È facile risolvere i problemi con le patch che non si sono verificate se le modifiche A e B sono ortogonali all'interno degli stessi file, ma concettualmente è ancora un po 'rischioso.

4 può dizzy ma è il modo più potente, flessibile e scalabile. Io default hg qinit con -c dato che voglio contrassegnare le patch work-in-progress e spingerle/tirarle, ma ci vuole un salto concettuale per rendersi conto che è possibile anche diramare in repo MQ.Ecco i passaggi (mq = hg --mq):

  1. hg qnew bugA; apportare modifiche per A; hg qref
  2. mq branch branchA; hg qci
  3. hg qpop; mq up -rtip^
  4. hg qnew bugB; apportare modifiche per B; hg qref
  5. mq branch branchB; hg qci
  6. di lavorare su un nuovo: hg qpop; mq up branchA; hg qpush

sembra folle di prendere tanti passi, e ogni volta che avete bisogno di cambiare lavoro è necessario hg qci; hg qpop; mq up <branch>; hg qpush. Considerate questo: avete diversi rami di rilascio con nome nello stesso repository, e avete bisogno di lavorare su diversi progetti e correzioni di bug allo stesso tempo per tutti loro (è meglio avere un bonus garantito per questo tipo di lavoro). Ti perderei molto presto con gli altri approcci.

Ora i miei amici hg amanti, ci sono altre/alternative migliori?


(UPDATE) qqueue quasi fa # 4 obsoleti. Vedere l'elegante descrizione di Steve Losh here.

+0

Come se non fosse ovvio, sto chiedendo un approccio basato su hg. Ma se puoi darmi un singolo comando da un altro SCM che funziona in tutti i casi, sto abbandonando hg. Consideralo una sfida, git aficionados. –

+1

Penso che sia necessario specificare il problema un po 'meglio. Perché questo non funziona in tutti i casi: correggi il bug A, commetti la sua patch, correggi bug B, commetti la sua patch? – Karmastan

+0

Mi dispiace di aver perso un po ', Spolsky-ishly. Ho menzionato un caso semplice: "se B è una correzione piccola e/o urgente". Una situazione più realistica è che A e B sono entrambi progetti lunghi che richiedono un sacco di commit e iterazioni. Inoltre, se hai una procedura di revisione del codice come fa la mia azienda, potresti aver terminato A e averlo inviato per la revisione, e non puoi stare lì a giocherellare con i pollici in attesa dei revisori, quindi devi andare a prendere B nel frattempo . Ma poi il revisore potrebbe far saltare il tuo codice e devi fare delle modifiche per compiacerlo/lei. Quindi ci possono essere innumerevoli interruttori di progetto lungo la strada. –

risposta

3

Sembra che non ci siano scelte più o migliori di quelle che ho elencato nella domanda. Quindi eccoli di nuovo.

  1. Utilizzare un clone per progetto.
    • Pro: separazione totale, quindi nessuna ricostruzione quando si passa da un progetto all'altro.
    • Contro: la toolchain deve passare tra due cloni.
  2. Utilizzare rami o segnalibri anonimi o denominati nello stesso repository.
    • Pro: pratica standard hg (o qualsiasi DVCS); pulito e chiaro.
    • Contro: deve eseguire il commit prima di passare e ricostruire dopo.
  3. Utilizzare MQ con una patch (o più patch consecutive) per progetto.
    • Pro: semplice e facile.
    • Contro: deve qrefresh prima di passare e ricostruire dopo; difficile e rischioso se i progetti non sono ortogonali.
  4. Utilizzare un ramo MQ (o qqueue in 1.6+) per progetto.
    • Pro: ultra flessibile e scalabile (per il numero di progetti simultanei)
    • Svantaggi: deve qrefresh e qcommit prima commutazione e ricostruire dopo; si sente complicato

Come sempre, non c'è nessun proiettile d'argento, in modo da scegliere quella giusta per il lavoro.


(UPDATE) Per chi è innamorato di MQ, utilizzando MQ in cima rami regolari (# 2 + # 3) è probabilmente la pratica più comune e preferibile.

Se si dispone di due progetti concorrenti con riferimento sulla due rami (ad esempio prossima release e versione attuale), è banale per hop tra loro in questo modo:

hg qnew; {coding}; hg qrefresh; {repeat} 
hg qfinish -a 
hg update -r <branch/bookmark/rev> 
hg qimport -r <rev>; {repeat} 

Per l'ultimo passo, qimport dovrebbe aggiungere un -a opzione per importare una linea di changeset in una sola volta. Spero che Meister Geisler avvisi questo :)

7

Userei sempre i rami denominati, perché questo consente a Mercurial di fare il suo lavoro: conservare la cronologia del progetto e ricordare perché hai apportato le modifiche in ordine al codice sorgente. Se avere un clone o due seduti sul vostro disco è generalmente facile, dato il mio stile di lavoro, almeno:

  1. Il vostro progetto manca un processo di generazione, in modo da poter testare ed eseguire cose giuste da il codice sorgente? Allora sarò tentato di avere un solo clone e hg up avanti e indietro quando ho bisogno di lavorare su un altro ramo.

  2. Ma se si dispone di un buildout, virtualenv o altra struttura che viene creata e che potrebbe divergere tra i due rami, quindi eseguire un hg up in attesa di rieseguire il processo di generazione può essere un grande problema, specialmente se sono coinvolte cose come la creazione di un database di esempio. In tal caso, utilizzerei sicuramente due cloni, uno seduto sulla punta del tronco e uno seduto sulla punta del ramo delle funzioni di emergenza.

+0

Ho votato ma non l'ho accettato perché ho menzionato il ramo di nome me stesso, e stavo chiedendo alternative :) Abbiamo un processo di compilazione (non molto moderno, come il codice di base stesso), quindi # 2 è davvero l'argomento per l'utilizzo di due cloni. Tuttavia, può essere difficile cambiare il flusso di lavoro di debug tra i due cloni, ad esempio avresti bisogno di due configurazioni di avvio in Eclipse. –

+1

In alternativa ai rami denominati, è possibile numerarli :) In altre parole, non esiste un'alternativa valida, ruota già inventata. –

1

Quindi la domanda è, nel punto in cui vi viene detto di smettere di lavorare in funzione di A, e cominciare indipendenti caratteristica B, quali opzioni alternative ci sono, per: come gestire lo sviluppo in concomitanza con Mercurial?

Esaminiamo il problema con la concorrenza rimossa, allo stesso modo in cui scrivete il codice filettato, definendo un flusso di lavoro semplice per risolvere qualsiasi problema a voi assegnato e applicarlo a ciascun problema. Mercurial si unirà al lavoro, una volta finito. Quindi, il programmatore A funzionerà sulla funzione A. Il programmatore B funzionerà sulla funzione B. Entrambi sono proprio voi. (Se solo avessimo cervelli multi-core :)

Vorrei sempre utilizzare i rami denominati, perché questo consente a Mercurial di fare il suo lavoro: mantenere la cronologia del progetto e ricordare perché hai apportato le modifiche in ordine il tuo codice sorgente

Sono d'accordo con il sentimento di Brandon, ma mi chiedo se abbia trascurato che la caratteristica A non è stata testata? Nel peggiore dei casi, il codice compila e supera i test unitari, ma alcuni metodi implementano i requisiti precedenti e alcuni metodi implementano quelli nuovi. Un diff contro il check-in precedente è lo strumento che utilizzerei per aiutarmi a tornare in pista con la funzione A.

Il tuo codice per la funzionalità A in un punto in cui normalmente lo verificherai? Passare dalla funzione A a lavorare sulla funzione B non è un motivo per impegnare il codice in testa o in una diramazione. Solo il codice di accesso che compila e passa i test. La ragione è che se il programmatore C ha bisogno di iniziare la funzione C, un nuovo checkout di questo ramo non è più il miglior punto di partenza. Mantenere le teste delle filiali in buona salute, significa che puoi rispondere rapidamente, con correzioni di errori più affidabili.

L'obiettivo è quello di eseguire il codice (verificato e verificato), in modo che si desideri che tutto il codice finisca nella fusione (dei rami di sviluppo e legacy). Il mio punto sembra essere, ho visto la ramificazione utilizzata in modo inefficiente: il codice diventa obsoleto e quindi non utilizzato, l'unione diventa più difficile del problema originale.

Solo la tua opzione 1 ha senso per me. In generale:

  1. Dovresti pensare che il tuo codice funzioni, prima che qualcun altro lo veda.
  2. Favorire la testa su un ramo.
  3. Filiale e check-in se qualcun altro sta riscontrando il problema.
  4. Succursale se il proprio sistema automatizzato o tester richiede solo il proprio codice.
  5. Succursale se fai parte di una squadra, stai lavorando su un problema. Consideralo come la testa, vedi 1-4.

Con l'eccezione dei file di configurazione, i processi di compilazione devono essere un checkout e un singolo comando di compilazione. Non dovrebbe essere più difficile passare da un clone a un altro programmatore per unirsi al progetto. (Ammetto che il mio progetto ha bisogno di un po 'di lavoro qui.)

+0

grazie per la risposta, ma sembra che la stai affrontando nel contesto di un VCS tradizionale (centralizzato) come svn. In hg (o qualsiasi DVCS) i tuoi commit sono locali fino a quando non vengono spinti/tirati, quindi è perfettamente sicuro (e consigliato) per eseguire il commit prima del test. –

+0

Principalmente uso svn, quindi sono sicuro che influisce sul mio punto di vista. In hg, dopo un push, il log mostra una differenza tra il commit push e il commit locale? Usi github? È fantastico, ma può essere difficile capire quale repository clonare - quale fork contiene le correzioni dei bug di cui hai bisogno, o che è attivo o abbandonato. Tornando alla ramificazione, posso vedere quanti punti di commit aiutano l'autore. Penso che tu stia dicendo che hg protegge gli altri sviluppatori dal vedere i commit intermedi, perché non vengono spinti. Penso che i test di integrazione siano dove questo potrebbe non essere vero. –

+0

scusa non ho notato il tuo commento. Una volta effettuati i commit (più accuratamente, i changeset), non sono più locali. Il log non mostra se è stato premuto un changeset. Il comando 'outgoing' ti dice se non lo ha fatto. Non c'è "protezione" sui commit locali. Chiunque può tirarli se vogliono. –

Problemi correlati