Dovresti fare entrambe le cose .
Inizia con la risposta accettata da @Norman: usa un repository con un ramo denominato per versione.
Quindi, disporre di un clone per ramo di rilascio per la creazione e il test.
Una nota chiave è che anche se usi più repository, dovresti evitare di usare transplant
per spostare i changeset tra loro perché 1) cambia hash e 2) può introdurre bug che sono molto difficili da rilevare quando ci sono cambiamenti contrastanti tra i changeset che trapianto e il ramo target. Invece vuoi fare la solita fusione (e senza premergere: ispeziona sempre visivamente la fusione), il che si tradurrà in ciò che @mg ha detto alla fine della sua risposta:
Il grafico potrebbe apparire diverso, ma ha la stessa struttura e il risultato finale è lo stesso.
Più verbalmente, se si utilizzano più repository, il repository "trunk" (o predefinito, principale, sviluppo, qualunque cosa) contenga TUTTI i changeset in TUTTI i repository. Ogni repository release / branch è semplicemente un ramo nel trunk, tutti uniti in un modo o nell'altro nel trunk, fino a quando non si desidera lasciare indietro una versione precedente. Pertanto, l'unica vera differenza tra quel repository principale e il singolo repository nello schema di filiale denominato è semplicemente se i rami sono denominati o meno.
Ciò dovrebbe rendere ovvio il motivo per cui ho detto "inizia con un repository". Quel singolo repository è l'unico posto in cui avrai mai bisogno di cercare eventuali cambiamenti in qualsiasi versione . È inoltre possibile taggare i changeset sui rami di rilascio per il versioning. È concettualmente chiaro e semplice e semplifica l'amministratore di sistema, poiché è l'unica cosa che deve assolutamente essere disponibile e recuperabile in ogni momento.
Tuttavia, è comunque necessario mantenere un clone per ramo / versione che è necessario creare e testare. È banale come puoi hg clone <main repo>#<branch> <branch repo>
, e quindi hg pull
nel repository di succursali estrarrà solo nuovi changeset su quel ramo (più i changeset di antenati sui rami precedenti che sono stati uniti).
Questa configurazione si adatta meglio al modello di commit puller del kernel Linux di single puller (non è bello comportarsi come Lord Linus. Nella nostra azienda chiamiamo integratore di ruoli ), poiché il repository principale è l'unica cosa che gli sviluppatori devono clonare e il l'estrattore deve entrare. La manutenzione dei repository delle filiali è puramente per la gestione dei rilasci e può essere completamente automatizzata. Gli sviluppatori non devono mai estrarre / spingere verso i repository delle filiali.
Ecco l'esempio di @ mg rifuso per questa configurazione. Punto di partenza:
[a] - [b]
Crea un ramo con nome per una versione di rilascio, ad esempio "1.0", quando arrivi alla versione alpha. Commit correzioni di bug su di esso:
[a] - [b] ------------------ [m1]
\ /
(1.0) - [x] - [y]
(1.0)
non è un vero changeset poiché il ramo denominato non esiste finché non si esegue il commit. (È possibile effettuare un commit banale, come l'aggiunta di un tag, per assicurarsi che i rami con nome siano creati correttamente.)
L'unione [m1]
è la chiave di questa configurazione. A differenza di un repository per sviluppatori in cui può esserci un numero illimitato di head, NON si desidera avere più head nel repository principale (ad eccezione del vecchio ramo dead release come menzionato in precedenza). Pertanto, ogni volta che si hanno nuovi changeset sui rami di rilascio, è necessario ricollegarli immediatamente al ramo di default (o un ramo di rilascio successivo). Ciò garantisce che qualsiasi correzione di bug in una versione sia inclusa anche in tutte le versioni successive.
Nel frattempo lo sviluppo sul ramo predefinito continua verso la prossima versione:
------- [c] - [d]
/
[a] - [b] ------------------ [m1]
\ /
(1.0) - [x] - [y]
E come al solito, devi unire le due teste sul ramo predefinito:
------- [c] - [d] -------
/ \
[a] - [b] ------------------ [m1] - [m2]
\ /
(1.0) - [x] - [y]
E questo è il clone del ramo 1.0:
[a] - [b] - (1.0) - [x] - [y]
Ora è un esercizio aggiungere il prossimo ramo di rilascio. Se è 2.0, si dirama sicuramente sul valore predefinito. Se è 1.1, puoi scegliere di diramare 1.0 o di default. Indipendentemente da ciò, qualsiasi nuovo changeset su 1.0 dovrebbe essere prima unito al ramo successivo, quindi al valore predefinito. Questo può essere fatto automaticamente se non ci sono conflitti, risultando in una semplice unione vuota.
Spero che l'esempio chiarisca i miei punti precedenti. In sintesi, i vantaggi di questo approccio sono:
- Repository autorevole unico che contiene il changeset completo e la cronologia delle versioni.
- Gestione delle versioni chiara e semplificata.
- Flusso di lavoro chiaro e semplificato per sviluppatori e integratori.
- Facilitare le iterazioni del flusso di lavoro (revisioni del codice) e l'automazione (unione vuota automatica).
AGGIORNAMENTO stesso hg fa questo : il repository principale contiene i rami predefiniti e stabili e il repository stabile è il clone dei rami stabili. Tuttavia, non utilizza il ramo con versione, poiché i tag di versione lungo il ramo stabile sono abbastanza buoni per i suoi scopi di gestione delle versioni.