Como você organiza seu repository de version control?

Primeiro, eu sei sobre isso: Como você organizaria um repository Subversion para projetos de software internos? Em seguida, a pergunta real: Minha equipe está reestruturando nosso repository e estou procurando dicas sobre como organizá-lo. (SVN neste caso). Aqui está o que nós inventamos. Temos um repository, vários projetos e várias referências cruzadas de svn: externals

\commonTools /*tools used in all projects. Referenced in each project with svn:externals*/ \NUnit.v2.4.8 \NCover.v.1.5.8 \ \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 único .exe)

É assim que planejamos distribuir o repository. A questão principal é que temos várias soluções, mas queremos compartilhar projetos entre soluções. Nós pensamos que não faz sentido mover esses projetos compartilhados para suas próprias soluções, e em vez disso decidimos usar o svn: externals para compartilhar projetos entre soluções. Também queremos manter um conjunto comum de ferramentas e bibliotecas de terceiros em um único lugar no repository, e elas fazem referência a elas 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, é o melhor que poderíamos pensar. Como você faria?

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

– coloque cada projeto em qualquer lugar no controle de origem, contanto que você preserve a estrutura do diretório raiz do projeto

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

– construa cada projeto completamente autônomo, contanto que você tenha access a suas dependencies binárias (diretórios locais “library” e “output”)

– construir e trabalhar com qualquer combinação de projetos, desde que sejam independentes

– construir e trabalhar com múltiplas cópias / versões de um único projeto, desde que sejam independentes

– evite entulhar seu repository de controle de origem com arquivos ou bibliotecas gerados

Eu recomendo (aqui está a carne):

  1. Defina cada projeto para produzir uma única entrega principal, como .DLL, .EXE ou .JAR (padrão com o Visual Studio).

  2. Estruture cada projeto como uma tree 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 NO dependencies em um IDE (mas não o impeça de ser construído no IDE, se possível).

  4. Considere nAnt para projetos .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 faça referência a suas dependencies externas (de terceiros) de um único diretório “library” compartilhado local, com todos esses binários TOTALMENTE identificados pela versão: %DirLibraryRoot%\ComponentA-1.2.3.4.dll %DirLibraryRoot%\ComponentB-5.6.7.8.dll , %DirLibraryRoot%\ComponentB-5.6.7.8.dll .

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

  7. Faça cada script de construção de projeto referenciar suas dependencies através de caminhos absolutos configuráveis ​​e totalmente versionados (veja acima) nos diretórios “library” e “output”, AND NO WHERE ELSE.

  8. NUNCA deixe um projeto referenciar diretamente outro projeto ou qualquer de seus conteúdos – apenas permita referências aos resultados principais no diretório “output” (veja acima).

  9. Faça com que cada script de construção de projeto faça referência a suas ferramentas de construção necessárias por um caminho absoluto configurável e totalmente versionado: %DirToolRoot%\ToolA\1.2.3.4 , %DirToolRoot%\ToolB\5.6.7.8 .

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

  11. SEMPRE requer um script de construção de projeto para referenciar TODOS os arquivos ou diretórios através de um caminho absoluto e configurável (enraizado em um diretório especificado por uma variável configurável): ${project.base.dir}/some/dirs ou ${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\here ou ..\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\here ou \\server\share\more\stuff\there .

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

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

  16. Em cada máquina, crie um script de shell que defina as variables ​​de ambiente necessárias, que são específicas para essa máquina (e possivelmente específicas para esse usuário, se relevante).

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

  18. NECESSITE cada script de construção do projeto para verificar cada uma de suas variables ​​de ambiente e abortar com uma mensagem significativa se elas não estiverem definidas.

  19. NECESSITE cada script de construção de projeto para verificar cada um de seus executáveis ​​de ferramentas de compilation dependentes, arquivos de biblioteca externos e arquivos de entrega de projeto dependentes e abortar com uma mensagem significativa se esses arquivos não existirem.

  20. RESISTE a tentação de comprometer QUALQUER arquivo gerado no controle de origem – sem entregáveis ​​do projeto, sem fonte 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 os 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 em estações de trabalho do desenvolvedor e em máquinas de compilation. Faça o backup, junto com seu repository de controle de origem.

  23. Estabeleça um servidor de continuous integration (máquina de compilation) sem ferramentas de desenvolvimento.

  24. Considere uma ferramenta para gerenciar suas bibliotecas externas e entregáveis, como o Ivy (usado com o Ant).

  25. NÃO use o Maven – ele inicialmente fará você feliz e eventualmente fará você chorar.

Note que nada disso é específico para o Subversion, e a maior parte é genérico para projetos direcionados a qualquer sistema operacional, hardware, plataforma, idioma, etc. Eu usei um pouco de syntax específica de ferramentas e sistemas operacionais, mas apenas para fins ilustrativos. -Eu confio que você irá traduzir para o seu sistema operacional ou ferramenta de escolha.

Nota adicional sobre as soluções do Visual Studio: não coloque-as 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 para criar / usar como achar melhor (mas não para o controle de origem). Eu mantenho um arquivo Rob.sln em minha estação de trabalho do qual faço referência a meu (s) projeto (s) atual (is). Como todos os 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 externals do Subversion (ou similar em outras ferramentas), eles são um antipadrão e, portanto, desnecessários.

Quando você implementa continuous integration, ou mesmo quando você quer apenas automatizar o processo de lançamento, crie um script para ele. Faça um script de shell único que: tome parâmetros do nome do projeto (conforme listado no repository) e nome da tag, crie um diretório temporário dentro de um diretório raiz configurável, faça checkout da origem para o nome do projeto e tag (construindo o URL apropriado no caso do Subversion) para esse diretório temporário, executa uma compilation 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 “build tools”. Seu servidor de continuous integration pode usar esse script como base para a criação de projetos ou pode até mesmo fornecê-lo (mas você ainda pode querer o seu próprio).

@VonC: Você NÃO quer trabalhar o tempo todo com “ant.jar” em vez de “ant-abcdjar” depois que você é gravado quando o script de construção quebra porque você inadvertidamente o executou com uma versão incompatível do Ant. Isso é particularmente comum entre o 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 compilation (Ant EFGH). Caso contrário, você acabará encontrando 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 na frente.

Eu acredito que o Pragmatic Version Control usando o Subversion tem tudo que você precisa para organizar seu repository.

Nós definimos o nosso para corresponder exatamente ao que você postou. Nós 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 suponha que não seja tão completo quanto o seu exemplo, funcionou bem para nós e nos permite manter as coisas separadas. Eu gosto da idéia de cada usuário ter uma pasta “Thrash” também – atualmente, esses tipos de projetos não acabam no controle Source, e eu sempre senti que eles deveriam.

Por que tudo isso em um repository? Por que não apenas ter um repository separado para cada projeto (quero dizer, “Solução”)?

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

E quantos projetos você planeja colocar nesse grande repository? 2? 3? 10? 100?

E o que você faz quando cancela o desenvolvimento de um projeto? Basta apagá-lo da tree do repository para que seja difícil encontrá-lo no futuro. Ou deixá-lo mentindo para sempre? Ou quando você quer mover um projeto para outro servidor?

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

Talvez eu seja tolo, mas gosto de um projeto por repository.

Acho que a principal desvantagem da estrutura proposta é que os projetos compartilhados serão versionados apenas com a primeira solução à qual foram adicionados (a menos que o svn: externals seja mais sofisticado do que eu imagino). Por exemplo, quando você cria uma ramificação para a primeira versão da Solução2, o Projeto1 não será ramificado, já que ele reside na Solução1. Se você precisar compilar a partir dessa ramificação posteriormente (versão QFE), ela usará a versão mais recente do Projeto1 em vez da versão do Projeto1 no momento da ramificação.

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 ramificá-los a cada versão de qualquer solução.

Para adicionar ao problema do caminho relativo:

Não tenho certeza se é um problema:
Basta fazer o checkout Solution1 / trunk no diretório chamado “Solution1”, idem para Solution2: o objective dos “diretórios” que realmente representam ramificações é não ficar visível depois de importado para uma área de trabalho. Portanto, caminhos relativos são possíveis entre ‘Solução1’ (na verdade ‘Solução1 / tronco’) e ‘Solução2’ (Solução2 / tronco).

RE: o caminho relativo e o problema do arquivo compartilhado –

Parece que isso é específico do svn, mas isso não é um problema. Uma outra pessoa já mencionou repositorys separados e essa é provavelmente a melhor solução em que posso pensar no caso de você ter projetos diferentes referindo-se a outros projetos arbitrários. No caso de você não ter arquivos compartilhados, a solução OP (assim como muitos outros) funcionará bem.

Ainda estamos trabalhando nisso e tenho 3 esforços diferentes (clientes diferentes) que tenho que resolver agora desde que assumi a configuração de version control inexistente ou insatisfatório.

Eu tenho um layout semelhante, mas meu tronco, ramos, tags todo o caminho no topo. Então: / trunk / main, / trunk / utils, / branches / release /, etc.

Isso acabou sendo muito útil quando queríamos experimentar outros sistemas de version control, porque muitas das ferramentas de tradução funcionavam melhor com o layout SVN do livro-texto básico.