Você deveria fazer as duas coisas .
Comece com a resposta aceita do @Norman: use um repositório com uma ramificação nomeada por release.
Em seguida, tenha um clone por ramificação de liberação para construção e teste.
Uma observação importante é que, mesmo se você usar vários repositórios, evite usá-lo transplant
para mover conjuntos de alterações entre eles, porque 1) altera o hash e 2) pode introduzir bugs que são muito difíceis de detectar quando há alterações conflitantes entre o conjunto de alterações que você transplante e ramo alvo. Em vez disso, você deseja fazer a mesclagem usual (e sem pré-emergir: sempre inspecione visualmente a mesclagem), o que resultará no que o mg disse no final de sua resposta:
O gráfico pode parecer diferente, mas tem a mesma estrutura e o resultado final é o mesmo.
Mais detalhadamente, se você usar vários repositórios, o repositório "trunk" (ou padrão, principal, desenvolvimento, qualquer que seja) contém TODOS os conjuntos de alterações em TODOS os repositórios. Cada repositório de release / ramificação é simplesmente uma ramificação no tronco, todas mescladas de uma maneira ou de outra para o tronco, até que você queira deixar uma versão antiga para trás. Portanto, a única diferença real entre esse repositório principal e o repositório único no esquema de ramificação nomeada é simplesmente se as ramificações são nomeadas ou não.
Isso deve tornar óbvio o motivo pelo qual eu disse "comece com um repo". Esse repositório único é o único lugar em que você precisará procurar por qualquer conjunto de alterações em qualquer versão . Você pode marcar ainda mais conjuntos de alterações nas ramificações da versão para controle de versão. É conceitualmente claro e simples, e simplifica a administração do sistema, pois é a única coisa que absolutamente precisa estar disponível e recuperável o tempo todo.
Mas ainda é necessário manter um clone por ramificação / versão que você precisa criar e testar. É trivial como você pode hg clone <main repo>#<branch> <branch repo>
e, hg pull
no repositório de ramificação, apenas puxa novos conjuntos de alterações nesse ramo (além de conjuntos de alterações ancestrais em ramos anteriores que foram mesclados).
Essa configuração se encaixa melhor no modelo de commit de kernel do linux de extrator único (não é bom agir como Lord Linus. Em nossa empresa, chamamos de integrador de funções ), pois o repositório principal é a única coisa que os desenvolvedores precisam clonar e extrator precisa puxar para dentro. A manutenção dos repositórios de filial é puramente para gerenciamento de liberação e pode ser completamente automatizada. Os desenvolvedores nunca precisam extrair / enviar para os repositórios de filial.
Aqui está o exemplo de @ mg reformulado para esta configuração. Ponto de partida:
[a] - [b]
Faça uma ramificação nomeada para uma versão de lançamento, diga "1.0", quando chegar à versão alfa. Cometa correções de erros:
[a] - [b] ------------------ [m1]
\ /
(1.0) - [x] - [y]
(1.0)
não é um conjunto de alterações real, pois o ramo nomeado não existe até você confirmar. (Você pode fazer uma confirmação trivial, como adicionar uma tag, para garantir que as ramificações nomeadas sejam criadas corretamente.)
A mesclagem [m1]
é a chave para essa configuração. Diferente de um repositório de desenvolvedores onde pode haver um número ilimitado de cabeças, você NÃO deseja ter várias cabeças no seu repositório principal (exceto o ramo antigo de liberação morta, como mencionado anteriormente). Portanto, sempre que houver novos conjuntos de alterações nas ramificações de liberação, você deverá mesclá-las novamente à ramificação padrão (ou uma ramificação de liberação posterior) imediatamente. Isso garante que qualquer correção de bug em uma versão também seja incluída em todas as versões posteriores.
Enquanto isso, o desenvolvimento na ramificação padrão continua na próxima versão:
------- [c] - [d]
/
[a] - [b] ------------------ [m1]
\ /
(1.0) - [x] - [y]
E, como sempre, você precisa mesclar as duas cabeças na ramificação padrão:
------- [c] - [d] -------
/ \
[a] - [b] ------------------ [m1] - [m2]
\ /
(1.0) - [x] - [y]
E este é o clone da ramificação 1.0:
[a] - [b] - (1.0) - [x] - [y]
Agora é um exercício para adicionar o próximo ramo de lançamento. Se for 2.0, ele definitivamente se desviará do padrão. Se for 1.1, você pode optar por ramificar 1.0 ou padrão. Independentemente disso, qualquer novo conjunto de alterações na 1.0 deve ser mesclado primeiro à próxima ramificação, depois para o padrão. Isso pode ser feito automaticamente se não houver conflito, resultando apenas em uma mesclagem vazia.
Espero que o exemplo esclareça meus pontos anteriores. Em resumo, as vantagens dessa abordagem são:
- Repositório autoritário único que contém conjunto de alterações completo e histórico de versões.
- Gerenciamento de versão claro e simplificado.
- Fluxo de trabalho claro e simplificado para desenvolvedores e integradores.
- Facilite as iterações do fluxo de trabalho (revisões de código) e a automação (mesclagem vazia automática).
UPDATE O próprio hg faz isso : o repositório principal principal contém as ramificações padrão e estável e o repositório estável é o clone da ramificação estável. Porém, ele não usa ramificação com versão, pois as tags de versão ao longo da ramificação estável são boas o suficiente para fins de gerenciamento de versão.