Como você organiza seu repositório de controle de versão?

108

Primeiro, eu sei sobre isso: como você organizaria um repositório Subversion para projetos de software internos? A seguir, a questão real: minha equipe está reestruturando nosso repositório e estou procurando dicas sobre como organizá-lo. (SVN neste caso). Aqui está o que descobrimos. Temos um repositório, vários projetos e várias referências cruzadas svn: externals

\commonTools /*tools used in all projects. Referenced in each project with svn:externals*/
   \NUnit.v2.4.8
   \NCover.v.1.5.8
   \<other similar tools>
\commonFiles /*settings strong name keys etc.*/
   \ReSharper.settings
   \VisualStudio.settings
\trash /*each member of the team has trash for samples, experiments etc*/
   \user1
   \user2
\projects
   \Solution1 /*Single actual project (Visual Studio Solution)*/
      \trunk
         \src
             \Project1 /*Each sub-project resulting in single .dll or .exe*/
             \Project2
         \lib
         \tools
         \tests
         \Solution1.sln
      \tags
      \branches
   \Solution2
      \trunk
         \src
             \Project3 /*Each sub-project resulting in single .dll or .exe*/
             \Project1 /*Project1 from Solution1 references with svn:externals*/
         \lib
         \tools
         \tests
         \Solution2.sln
      \tags
      \branches

Para limpar o vocabulário: Solução significa produto único, Projeto é um Projeto do Visual Studio (que resulta em um único .dll ou .exe)

É assim que planejamos fazer o layout do repositório. O principal problema é que temos várias soluções, mas queremos compartilhar projetos entre soluções. Achamos que realmente não havia sentido em mover esses projetos compartilhados para suas próprias soluções e, em vez disso, decidimos usar svn: externals para compartilhar projetos entre soluções. Também queremos manter um conjunto comum de ferramentas e bibliotecas de terceiros em um lugar no repositório, e eles fazem referência a eles em cada Solução com svn: externals.

O que você acha desse layout? Especialmente sobre o uso de svn: externals. Não é uma solução ideal, mas considerando todos os prós e contras, é a melhor que poderíamos imaginar. Como você faria?

Krzysztof Kozmic
fonte
Tem certeza que quer dizer "thrash"? Ou melhor, "lixo"?
ssc de

Respostas:

92

Se você seguir minhas recomendações abaixo (eu faço há anos), você será capaz de:

- coloque cada projeto em qualquer lugar no controle de origem, desde que você preserve a estrutura do diretório raiz do projeto para baixo

- construir cada projeto em qualquer lugar em qualquer máquina, com risco mínimo e preparação mínima

- construir cada projeto completamente autônomo, contanto que você tenha acesso às suas dependências binárias (diretórios "biblioteca" e "saída" locais)

- construir e trabalhar com qualquer combinação de projetos, uma vez que são independentes

- construir e trabalhar com várias cópias / versões de um único projeto, uma vez que são independentes

- evite sobrecarregar seu repositório de controle de origem com arquivos ou bibliotecas gerados

Eu recomendo (aqui está a carne):

  1. Defina cada projeto para produzir um único produto primário, como .DLL, .EXE ou .JAR (padrão com Visual Studio).

  2. Estruture cada projeto como uma árvore de diretórios com uma única raiz.

  3. Crie um script de construção automatizado para cada projeto em seu diretório raiz que o construirá do zero, sem dependências em um IDE (mas não evite que seja construído no IDE, se possível).

  4. Considere projetos nAnt para .NET no Windows ou algo semelhante com base em seu sistema operacional, plataforma de destino, etc.

  5. Faça com que cada script de construção de projeto referencie suas dependências externas (de terceiros) de um único diretório de "biblioteca" local compartilhado, com cada binário TOTALMENTE identificado pela versão: %DirLibraryRoot%\ComponentA-1.2.3.4.dll, %DirLibraryRoot%\ComponentB-5.6.7.8.dll.

  6. Faça com que cada script de construção de projeto publique a entrega primária em um único diretório de "saída" compartilhado local: %DirOutputRoot%\ProjectA-9.10.11.12.dll, %DirOutputRoot%\ProjectB-13.14.15.16.exe.

  7. Faça com que cada script de construção de projeto referencie suas dependências por meio de caminhos absolutos configuráveis ​​e com versão completa (veja acima) nos diretórios "library" e "output", E NENHUM ONDE MAIS.

  8. NUNCA deixe um projeto referenciar diretamente outro projeto ou qualquer um de seus conteúdos - permita apenas referências aos produtos primários no diretório "output" (veja acima).

  9. Faça com que cada script de construção de projeto referencie suas ferramentas de construção necessárias por meio de um caminho absoluto configurável e com versão completa: %DirToolRoot%\ToolA\1.2.3.4, %DirToolRoot%\ToolB\5.6.7.8.

  10. Faça cada projeto script de construção de conteúdo fonte de referência por um caminho absoluto relativo ao diretório raiz do projeto: ${project.base.dir}/src, ${project.base.dir}/tst(sintaxe varia de acordo com ferramenta de construção).

  11. SEMPRE exija um script de construção de projeto para fazer referência a CADA arquivo ou diretório por meio de um caminho absoluto e configurável (com raiz em um diretório especificado por uma variável configurável): ${project.base.dir}/some/dirsou ${env.Variable}/other/dir.

  12. NUNCA permita que um script de construção de projeto faça referência a QUALQUER COISA com um caminho relativo como .\some\dirs\hereou ..\some\more\dirs, SEMPRE use caminhos absolutos.

  13. NUNCA permita que um script de construção de projeto faça referência a QUALQUER COISA usando um caminho absoluto que não tenha um diretório raiz configurável, como C:\some\dirs\hereou \\server\share\more\stuff\there.

  14. Para cada diretório raiz configurável referenciado por um script de construção de projeto, defina uma variável de ambiente que será usada para essas referências.

  15. Tente minimizar o número de variáveis ​​de ambiente que você deve criar para configurar cada máquina.

  16. Em cada máquina, crie um script de shell que define as variáveis ​​de ambiente necessárias, que é específico para ESSA máquina (e possivelmente específico para aquele usuário, se relevante).

  17. NÃO coloque o script de shell de configuração específico da máquina no controle de origem; em vez disso, para cada projeto, envie uma cópia do script no diretório raiz do projeto como um modelo.

  18. REQUIRE cada script de construção de projeto para verificar cada uma de suas variáveis ​​de ambiente e aborte com uma mensagem significativa se não estiverem definidas.

  19. REQUIRE cada script de construção de projeto para verificar cada um de seus executáveis ​​de ferramenta de construção dependentes, arquivos de biblioteca externa e arquivos de entrega de projeto dependentes, e aborte com uma mensagem significativa se esses arquivos não existirem.

  20. RESISTA à tentação de comprometer QUALQUER arquivo gerado no controle de origem - sem entregas do projeto, sem origem gerada, sem documentos gerados, etc.

  21. Se você usar um IDE, gere todos os arquivos de controle de projeto que puder e não os envie para o controle de origem (isso inclui arquivos de projeto do Visual Studio).

  22. Estabeleça um servidor com uma cópia oficial de todas as bibliotecas e ferramentas externas, para ser copiado / instalado nas estações de trabalho do desenvolvedor e nas máquinas de construção. Faça backup dele, junto com seu repositório de controle de origem.

  23. Estabeleça um servidor de integração contínua (máquina de construção) SEM qualquer ferramenta de desenvolvimento.

  24. Considere uma ferramenta para gerenciar suas bibliotecas externas e produtos, como Ivy (usado com Ant).

  25. NÃO use o Maven - ele inicialmente o deixará feliz e, eventualmente, o fará chorar.

Observe que nada disso é específico do Subversion, e a maioria é genérico para projetos direcionados a qualquer sistema operacional, hardware, plataforma, linguagem, etc. Eu usei um pouco de sintaxe específica do sistema operacional e da ferramenta, mas apenas para ilustração- -Eu confio que você traduzirá para o seu sistema operacional ou ferramenta de escolha.

Observação adicional sobre as soluções do Visual Studio: não as coloque no controle de origem! Com essa abordagem, você não precisa deles ou pode gerá-los (assim como os arquivos de projeto do Visual Studio). No entanto, acho melhor deixar os arquivos da solução para desenvolvedores individuais criarem / usarem como acharem adequado (mas não registrados no controle de origem). Eu mantenho um Rob.slnarquivo em minha estação de trabalho do qual faço referência aos meus projetos atuais. Como meus projetos são independentes, posso adicionar / remover projetos à vontade (isso significa que não há referências de dependência baseadas em projeto).

Por favor, não use recursos externos do Subversion (ou similares em outras ferramentas), eles são um antipadrão e, portanto, desnecessários.

Quando você implementa a integração contínua, ou mesmo quando deseja apenas automatizar o processo de liberação, crie um script para ela. Faça um único script de shell que: pegue os parâmetros do nome do projeto (conforme listado no repositório) e nome da tag, crie um diretório temporário dentro de um diretório raiz configurável, verifique a fonte para o nome do projeto e nome da tag fornecidos (construindo o URL apropriada no caso do Subversion) para aquele diretório temporário, executa uma compilação limpa que executa testes e empacota a entrega. Este script de shell deve funcionar em qualquer projeto e deve ser verificado no controle de origem como parte de seu projeto de "ferramentas de construção". Seu servidor de integração contínua pode usar esse script como sua base para a construção de projetos ou pode até fornecê-lo (mas você ainda pode querer o seu próprio).

@VonC: Você NÃO deseja trabalhar o tempo todo com "ant.jar" ao invés de "ant-abcdjar" depois de ser queimado quando seu script de construção quebra porque você, sem saber, o executou com uma versão incompatível do Ant. Isso é particularmente comum entre Ant 1.6.5 e 1.7.0. Generalizando, você SEMPRE deseja saber qual versão específica de CADA componente está sendo usada, incluindo sua plataforma (Java ABCD) e sua ferramenta de construção (Ant EFGH). Caso contrário, você eventualmente encontrará um bug e seu primeiro GRANDE problema será rastrear quais versões de seus vários componentes estão envolvidas. É simplesmente melhor resolver esse problema antecipadamente.

Rob Williams
fonte
6
Tantos pontos a criticar ... basta dizer que esta não é uma receita universal! Os pontos 5 e 6 em particular estão errados quando o projeto é grande e o número de terceiros é importante: você deseja trabalhar o tempo todo com 'ant.jar', não 'ant1.5.4.jar' ou o produto myProduct .exe, não 1.3.exe
VonC
5
Ainda assim, +1 para muitos outros pontos que você está fazendo, que são válidos e falam muito sobre sua vasta experiência no assunto.
VonC
3
Eu adoraria ouvir e interagir com suas críticas - cada ponto é baseado na resolução de experiências ruins em grandes projetos. Por exemplo, abordar o problema de quais versões são representadas por Xxx.jar e Yyy.exe, especialmente quando há literalmente uma dúzia de cópias sendo referenciadas.
Rob Williams de
2
@Rob - Você pode explicar melhor o seu tema 'antipadrão externo'? Eu a levantei como uma questão aqui: stackoverflow.com/questions/338824/…
Ken
3
@Makis: Você estaria correto, IF # 12 não foi balanceado por # 13. Cada referência a um arquivo ou diretório dentro de cada projeto deve ser feita por meio de um caminho absoluto que começa com uma variável de diretório raiz configurável, por exemplo, $ {basedir} /sub/dir/file.txt no Ant.
Rob Williams
3

Configuramos o nosso para corresponder quase exatamente ao que você postou. Usamos a forma geral:

\Project1
   \Development (for active dev - what you've called "Trunk", containing everything about a project)
   \Branches (For older, still-evolving supported branches of the code)
       \Version1
       \Version1.1
       \Version2
   \Documentation (For any accompanying documents that aren't version-specific

Embora eu não suponha que seja tão completo quanto seu exemplo, funcionou bem para nós e nos permite manter as coisas separadas. Eu gosto da ideia de cada usuário ter uma pasta "Thrash" também - atualmente, esses tipos de projetos não terminam no controle de código-fonte, e sempre achei que deveriam.

SqlRyan
fonte
3
Estou surpreso que você tenha um diretório separado para documentos que não mudam entre as versões ... Nunca tive o prazer de trabalhar em tal produto! :)
ARKBAN
1

Por que ter tudo em um repositório? Por que não apenas ter um repositório separado para cada projeto (quero dizer "Solução")?

Bem, pelo menos eu usei a abordagem de um projeto por repositório. Sua estrutura de repositório parece complicada demais para mim.

E quantos projetos você planeja colocar neste grande repositório? 2? 3? 10? 100?

E o que você faz quando cancela o desenvolvimento de um projeto? Basta excluí-lo da árvore do repositório para que seja difícil encontrá-lo no futuro. Ou deixá-lo deitado para sempre? Ou quando você deseja mover um projeto para outro servidor completamente?

E a bagunça de todos esses números de versão? Os números da versão de um projeto vão como 2, 10, 11, enquanto o outro vai como 1, 3, 4, 5, 6, 7, 8, 9, 12 ...

Talvez eu seja tolo, mas gosto de um projeto por repositório.

Rene Saarsoo
fonte
1. Um repositório é uma política da empresa, não posso mudar isso. 2. Teremos cerca de uma dúzia de soluções. 3. por números de versão, você quer dizer revisões? Isso não é um problema para nós.
Krzysztof Kozmic,
Uma boa estrutura de projeto deve ignorar o resto da estrutura do repositório, particularmente no que diz respeito a um ou mais repositórios. Por favor, veja minha resposta detalhada.
Rob Williams de
1
Observe que ter vários repositórios em muitas (a maioria?) Ferramentas de controle de origem pode ser MUITO caro, como quando você implementa a segurança.
Rob Williams de
0

Eu acho que a principal desvantagem da estrutura proposta é que os projetos compartilhados serão versionados apenas com a primeira solução à qual eles foram adicionados (a menos que svn: externals seja mais sofisticado do que estou imaginando). Por exemplo, quando você cria uma ramificação para a primeira versão da Solução2, Projeto1 não será ramificada, pois reside na Solução1. Se você precisar construir a partir desse branch posteriormente (versão QFE), ele usará a versão mais recente do Projeto1 em vez da versão do Projeto1 no momento do branch.

Por esse motivo, pode ser vantajoso colocar os projetos compartilhados em uma ou mais soluções compartilhadas (e, portanto, diretórios de nível superior em sua estrutura) e, em seguida, ramificá-los a cada lançamento de qualquer solução.

C. Dragon 76
fonte
Você está certo até certo ponto. Mas podemos atualizar a referência se quisermos. E colocar projetos compartilhados em sua própria solução também não faz muito sentido. Embora eu adorasse encontrar uma solução melhor do que svn: externals em todos os lugares.
Krzysztof Kozmic,
O que você quer dizer com "atualizar a referência se quisermos"? Não vejo como você seria capaz de ramificar o Projeto1 (o que parece desejável sempre que você ramificar a Solução2) sem ramificar a Solução1.
C. Dragon 76,
Por favor, veja minha resposta detalhada, especialmente para NÃO colocar as soluções do Visual Studio no controle de origem.
Rob Williams de
0

Para aumentar o problema do caminho relativo:

Não tenho certeza se é um problema:
Basta verificar a Solution1 / trunk no diretório chamado "Solution1", idem para a Solution2: o objetivo dos 'diretórios' que realmente representam os ramos é não ficarem visíveis depois de importados para um espaço de trabalho. Portanto, caminhos relativos são possíveis entre 'Solução1' (na verdade, 'Solução1 / tronco') e 'Solução2' (Solução2 / tronco).

VonC
fonte
Isso quebraria muito facilmente, consulte minha resposta detalhada.
Rob Williams de
0

RE: o caminho relativo e o problema de arquivo compartilhado -

Parece que isso é específico do svn, mas isso não é um problema. Outra pessoa já mencionou repositórios separados e essa é provavelmente a melhor solução que posso pensar no caso em que você tem projetos diferentes referindo-se a outros projetos arbitrários. Caso você não tenha arquivos compartilhados, a solução OP (assim como muitas outras) funcionará bem.

Ainda estamos trabalhando nisso e tenho 3 esforços diferentes (clientes diferentes) que tenho que resolver agora, uma vez que assumi a configuração do controle de versão inexistente ou ruim.

Tim
fonte
Ter projetos referenciando outros projetos cria um pesadelo de manutenção porque as dependências crescem exponencialmente e as referências são MUITO frágeis. Por favor, veja minha resposta detalhada.
Rob Williams de
0

Eu tenho um layout semelhante, mas meu tronco, galhos e etiquetas estão no topo. Portanto: / trunk / main, / trunk / utils, / branches / release /, etc.

Isso acabou sendo muito útil quando queríamos experimentar outros sistemas de controle de versão porque muitas das ferramentas de tradução funcionavam melhor com o layout SVN básico do livro didático.

Peter Mortensen
fonte