¿Cómo organiza su repositorio de control de versiones?

108

Primero, sé esto: ¿Cómo organizarías un repositorio de Subversion para proyectos de software internos? A continuación, la pregunta real: mi equipo está reestructurando nuestro repositorio y estoy buscando sugerencias sobre cómo organizarlo. (SVN en este caso). Esto es lo que se nos ocurrió. Tenemos un repositorio, múltiples proyectos y múltiples svn: referencias cruzadas externas

\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 borrar el vocabulario: solución significa producto único, Project es un proyecto de Visual Studio (que da como resultado un solo .dll o un solo .exe)

Así es como planeamos diseñar el repositorio. El problema principal es que tenemos múltiples Soluciones, pero queremos compartir Proyectos entre Soluciones. Pensamos que realmente no tiene sentido mover esos Proyectos compartidos a sus propias Soluciones, y en su lugar decidimos usar svn: externals para compartir Proyectos entre Soluciones. También queremos mantener un conjunto común de herramientas y bibliotecas de terceros en un lugar del repositorio, y hacer referencia a ellos en cada Solución con svn: externals.

¿Qué opinas de este diseño? Especialmente sobre el uso de svn: externals. No es una solución ideal, pero considerando todos los pros y los contras, es lo mejor que se nos ocurre. ¿Como lo harias?

Krzysztof Kozmic
fuente
¿Estás seguro de que te refieres a "thrash"? ¿O más bien "basura"?
ssc

Respuestas:

92

Si sigue mis recomendaciones a continuación (las he hecho durante años), podrá:

- coloque cada proyecto en cualquier lugar del control de código fuente, siempre que conserve la estructura desde el directorio raíz del proyecto hacia abajo

- construya cada proyecto en cualquier lugar de cualquier máquina, con un riesgo mínimo y una preparación mínima

- construya cada proyecto de forma completamente independiente, siempre que tenga acceso a sus dependencias binarias ("biblioteca" local y directorios de "salida")

- construir y trabajar con cualquier combinación de proyectos, ya que son independientes

- construir y trabajar con múltiples copias / versiones de un solo proyecto, ya que son independientes

- evite saturar su repositorio de control de fuente con archivos o bibliotecas generados

Recomiendo (aquí está la carne):

  1. Defina cada proyecto para producir una única entrega principal, como .DLL, .EXE o .JAR (predeterminado con Visual Studio).

  2. Estructure cada proyecto como un árbol de directorios con una sola raíz.

  3. Cree un script de compilación automatizado para cada proyecto en su directorio raíz que lo compile desde cero, SIN dependencias en un IDE (pero no impida que se compile en el IDE, si es posible).

  4. Considere nAnt para proyectos .NET en Windows, o algo similar según su sistema operativo, plataforma de destino, etc.

  5. Haga que cada script de construcción del proyecto haga referencia a sus dependencias externas (de terceros) desde un único directorio de "biblioteca" compartido local, con cada uno de esos binarios TOTALMENTE identificado por la versión: %DirLibraryRoot%\ComponentA-1.2.3.4.dll, %DirLibraryRoot%\ComponentB-5.6.7.8.dll.

  6. Haga que cada script de construcción de proyecto publique la entrega principal en un único directorio de "salida" compartido local: %DirOutputRoot%\ProjectA-9.10.11.12.dll, %DirOutputRoot%\ProjectB-13.14.15.16.exe.

  7. Haga que cada script de construcción de proyecto haga referencia a sus dependencias a través de rutas absolutas configurables y completamente versionadas (ver arriba) en los directorios "biblioteca" y "salida", Y EN NINGÚN OTRO LUGAR.

  8. NUNCA permita que un proyecto haga referencia directamente a otro proyecto o cualquiera de sus contenidos; solo permita referencias a los entregables primarios en el directorio de "salida" (ver arriba).

  9. Haga que cada secuencia de comandos de compilación del proyecto haga referencia a sus herramientas de compilación necesarias mediante una ruta absoluta configurable y completamente versionada: %DirToolRoot%\ToolA\1.2.3.4, %DirToolRoot%\ToolB\5.6.7.8.

  10. Hacer todos los proyectos del script de creación de contenido de código de referencia mediante una ruta absoluta relativa al directorio raíz del proyecto: ${project.base.dir}/src, ${project.base.dir}/tst(sintaxis varía según la herramienta de construcción).

  11. SIEMPRE requiera un script de construcción del proyecto para hacer referencia a CADA archivo o directorio a través de una ruta absoluta y configurable (enraizada en un directorio especificado por una variable configurable): ${project.base.dir}/some/dirso ${env.Variable}/other/dir.

  12. NUNCA permita que un script de construcción de proyecto haga referencia a NADA con una ruta relativa como .\some\dirs\hereo ..\some\more\dirs, SIEMPRE use rutas absolutas.

  13. NUNCA permita que un script de construcción de proyecto haga referencia a NADA utilizando una ruta absoluta que no tenga un directorio raíz configurable, como C:\some\dirs\hereo \\server\share\more\stuff\there.

  14. Para cada directorio raíz configurable al que hace referencia un script de construcción del proyecto, defina una variable de entorno que se utilizará para esas referencias.

  15. Intente minimizar la cantidad de variables de entorno que debe crear para configurar cada máquina.

  16. En cada máquina, cree un script de shell que defina las variables de entorno necesarias, que es específico para ESA máquina (y posiblemente específico para ese usuario, si es relevante).

  17. NO coloque el script de shell de configuración específico de la máquina en el control de código fuente; en su lugar, para cada proyecto, envíe una copia del script en el directorio raíz del proyecto como plantilla.

  18. REQUIERE que cada script de construcción del proyecto verifique cada una de sus variables de entorno y anule con un mensaje significativo si no están definidas.

  19. REQUIERE que cada secuencia de comandos de compilación del proyecto verifique cada uno de los ejecutables de la herramienta de compilación dependiente, los archivos de la biblioteca externa y los archivos entregables del proyecto dependiente, y anule con un mensaje significativo si esos archivos no existen.

  20. RESISTE la tentación de comprometer CUALQUIER archivo generado en el control de la fuente: sin entregables del proyecto, sin fuente generada, sin documentos generados, etc.

  21. Si usa un IDE, genere todos los archivos de control del proyecto que pueda y no los confíe al control de código fuente (esto incluye los archivos de proyecto de Visual Studio).

  22. Establezca un servidor con una copia oficial de todas las bibliotecas y herramientas externas, que se copiará / instalará en las estaciones de trabajo de los desarrolladores y en las máquinas de construcción. Realice una copia de seguridad junto con su repositorio de control de código fuente.

  23. Establezca un servidor de integración continua (máquina de construcción) SIN herramientas de desarrollo en absoluto.

  24. Considere una herramienta para administrar sus bibliotecas externas y entregables, como Ivy (utilizada con Ant).

  25. NO uses Maven: inicialmente te hará feliz y eventualmente te hará llorar.

Tenga en cuenta que nada de esto es específico de Subversion, y la mayoría es genérico para proyectos dirigidos a cualquier sistema operativo, hardware, plataforma, lenguaje, etc. Usé un poco de sintaxis específica del sistema operativo y de la herramienta, pero solo como ilustración. -Confío en que lo traducirá a su sistema operativo o herramienta de elección.

Nota adicional sobre las soluciones de Visual Studio: ¡no las ponga en control de código fuente! Con este enfoque, no los necesita en absoluto o puede generarlos (al igual que los archivos de proyecto de Visual Studio). Sin embargo, creo que es mejor dejar los archivos de la solución a los desarrolladores individuales para que los creen / usen como mejor les parezca (pero no registrados en el control de código fuente). Yo guardo unRob.sln archivo en mi estación de trabajo desde el que hago referencia a mis proyectos actuales. Dado que todos mis proyectos son independientes, puedo agregar / eliminar proyectos a voluntad (eso significa que no hay referencias de dependencia basadas en proyectos).

No utilice externos de Subversion (o similares en otras herramientas), son un anti-patrón y, por lo tanto, innecesarios.

Cuando implemente la integración continua, o incluso cuando solo desee automatizar el proceso de lanzamiento, cree un script para ello. Cree un único script de shell que: tome parámetros del nombre del proyecto (como se enumeran en el repositorio) y el nombre de la etiqueta, cree un directorio temporal dentro de un directorio raíz configurable, verifique la fuente para el nombre del proyecto y el nombre de la etiqueta dados (construyendo el URL apropiada en el caso de Subversion) a ese directorio temporal, realiza una compilación limpia que ejecuta pruebas y empaqueta el entregable. Este script de shell debería funcionar en cualquier proyecto y debería incorporarse al control de código fuente como parte de su proyecto de "herramientas de construcción". Su servidor de integración continua puede utilizar este script como base para la creación de proyectos, o incluso puede proporcionarlo (pero es posible que desee el suyo propio).

@VonC: NO desea trabajar en todo momento con "ant.jar" en lugar de "ant-abcdjar" después de que se queme cuando se rompe el script de compilación porque, sin saberlo, lo ejecutó con una versión incompatible de Ant. Esto es particularmente común entre Ant 1.6.5 y 1.7.0. Generalizando, SIEMPRE desea saber qué versión específica de CADA componente se está utilizando, incluida su plataforma (Java ABCD) y su herramienta de compilación (Ant EFGH). De lo contrario, eventualmente encontrará un error y su primer GRAN problema será rastrear qué versiones de sus diversos componentes están involucradas. Simplemente, es mejor resolver ese problema desde el principio.

Rob Williams
fuente
6
Tantos puntos para criticar ... ¡basta con decir que esta no es una receta universal! Los puntos 5 y 6 en particular son tan erróneos cuando el proyecto es grande y la cantidad de terceros es importante: desea trabajar en todo momento con 'ant.jar', no con 'ant1.5.4.jar' o el producto myProduct .exe, no 1.3.exe
VonC
5
Aún así, +1 para muchos otros puntos que está haciendo que son válidos y hablan muy bien de su vasta experiencia en el tema.
VonC
3
Me encantaría escuchar e interactuar con tus críticas, todos y cada uno de los puntos se basan en la resolución de malas experiencias con grandes proyectos. Por ejemplo, abordar el problema de qué versiones están representadas por Xxx.jar y Yyy.exe, especialmente cuando hay literalmente una docena de copias a las que se hace referencia.
Rob Williams
2
@Rob - ¿Puedes dar más detalles sobre tu tema de 'antipatrón externo'? Lo planteé como una pregunta aquí: stackoverflow.com/questions/338824/…
Ken
3
@Makis: Estarías en lo correcto, SI el # 12 no estuviera equilibrado con el # 13. Cada referencia a un archivo o directorio dentro de cada proyecto debe hacerse a través de una ruta absoluta que comience con una variable de directorio raíz configurable, por ejemplo, $ {basedir} /sub/dir/file.txt en Ant.
Rob Williams
3

Hemos configurado el nuestro para que coincida casi exactamente con lo que ha publicado. Usamos la forma general:

\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

Aunque supongo que no es tan completo como su ejemplo, nos ha funcionado bien y nos permite mantener las cosas separadas. Me gusta la idea de que cada usuario también tenga una carpeta "Thrash"; actualmente, ese tipo de proyectos no terminan en el control de código fuente, y siempre sentí que deberían hacerlo.

SqlRyan
fuente
3
Me sorprende que tenga un directorio separado para documentos que no cambian entre versiones ... ¡Nunca tuve el placer de trabajar en un producto así! :)
ARKBAN
1

¿Por qué tenerlo todo en un repositorio? ¿Por qué no tener un repositorio separado para cada proyecto (me refiero a "Solución")?

Bueno, al menos me he acostumbrado al enfoque de un proyecto por repositorio. Su estructura de repositorio me parece demasiado complicada.

¿Y cuántos proyectos planeas poner en este gran repositorio? 2? 3? 10? 100?

¿Y qué haces cuando cancelas el desarrollo de un proyecto? Simplemente elimínelo del árbol del repositorio para que sea difícil de encontrar en el futuro. ¿O dejarlo tirado para siempre? ¿O cuando quiere mover un proyecto a otro servidor por completo?

¿Y qué hay del lío de todos esos números de versión? Los números de versión de un proyecto son 2, 10, 11, mientras que el otro es 1, 3, 4, 5, 6, 7, 8, 9, 12 ...

Quizás soy tonto, pero me gusta un proyecto por repositorio.

Rene Saarsoo
fuente
1. Un repositorio es una política de la empresa, eso no se puede cambiar. 2. Tendremos alrededor de una docena de Soluciones. 3. ¿Por números de versión te refieres a revisiones? Eso no es un problema para nosotros.
Krzysztof Kozmic
Una buena estructura de proyecto debe ser ajena al resto de la estructura del repositorio, particularmente con respecto a uno o varios repositorios. Por favor vea mi respuesta detallada.
Rob Williams
1
Tenga en cuenta que tener varios repositorios en muchas (¿la mayoría?) De las herramientas de control de código fuente puede ser MUY caro, como cuando se implementa la seguridad.
Rob Williams
0

Creo que la principal desventaja de la estructura propuesta es que los proyectos compartidos solo serán versionados con la primera solución a la que fueron agregados (a menos que svn: externals sea más elegante de lo que imagino). Por ejemplo, cuando crea una rama para la primera versión de Solution2, Project1 no se ramificará ya que reside en Solution1. Si necesita compilar desde esa rama en un momento posterior (versión QFE), utilizará la última versión de Project1 en lugar de la versión de Project1 en el momento de la rama.

Por esta razón, puede ser ventajoso colocar los proyectos compartidos en una o más soluciones compartidas (y por lo tanto, directorios de nivel superior en su estructura) y luego bifurcarlos con cada lanzamiento de cualquier solución.

C. Dragón 76
fuente
Tiene razón hasta cierto punto. Pero podemos actualizar la referencia si queremos. Y poner Proyectos compartidos en su propia Solución tampoco tiene mucho sentido. Aunque me encantaría encontrar una solución mejor que svn: externals por todas partes.
Krzysztof Kozmic
¿Qué quieres decir con "actualizar la referencia si queremos"? No veo cómo podría bifurcar Project1 (que parece deseable cada vez que bifurca Solution2) sin bifurcar Solution1.
C.Dragon 76
Consulte mi respuesta detallada, en particular para NO poner las soluciones de Visual Studio en el control de código fuente.
Rob Williams
0

Para agregar al problema de la ruta relativa:

No estoy seguro de que sea un problema:
simplemente revise Solution1 / trunk en el directorio llamado "Solution1", lo mismo para Solution2: el objetivo de los 'directorios' que realmente representan ramas es que no sean visibles una vez importados a un espacio de trabajo. Por lo tanto, las rutas relativas son posibles entre 'Solución1' (en realidad 'Solución1 / troncal') y 'Solución2' (Solución2 / troncal).

VonC
fuente
Esto se rompería muy fácilmente, consulte mi respuesta detallada.
Rob Williams
0

RE: la ruta relativa y el problema del archivo compartido -

Parece que esto es específico de svn, pero eso no es un problema. Otra persona ya mencionó repositorios separados y esa es probablemente la mejor solución que se me ocurre en el caso de que tenga diferentes proyectos que se refieran a otros proyectos arbitrarios. En el caso de que no tenga archivos compartidos, la solución OP (así como muchas otras) funcionará bien.

Todavía estamos trabajando en esto y tengo 3 esfuerzos diferentes (clientes diferentes) que tengo que resolver ahora mismo desde que asumí la configuración del control de versiones inexistente o deficiente.

Tim
fuente
Tener proyectos haciendo referencia a otros proyectos crea una pesadilla de mantenimiento porque las dependencias crecen exponencialmente y las referencias son MUY frágiles. Por favor vea mi respuesta detallada.
Rob Williams
0

Tengo un diseño similar, pero mi tronco, ramas, etiquetas en la parte superior. Entonces: / trunk / main, / trunk / utils, / branch / release /, etc.

Esto terminó siendo realmente útil cuando queríamos probar otros sistemas de control de versiones porque muchas de las herramientas de traducción funcionaban mejor con el diseño básico SVN de los libros de texto.

Peter Mortensen
fuente