svn - tortoise - Diseño del repositorio de Subversion
svn tortoise (6)
La mayoría de las herramientas de subversión crean un diseño de repositorio predeterminado con / trunk, / branches y / tags. La documentación también recomienda no usar repositorios separados para cada proyecto, por lo que el código se puede compartir más fácilmente.
Seguir ese consejo me llevó a tener un repositorio con el siguiente diseño:
/trunk /Project1 /Project2 /branches /Project1 /Project2 /tags /Project1 /Project2
y así sucesivamente, entiendes la idea. Con el tiempo, encontré esta estructura un poco torpe y se me ocurrió que hay una interpretación alternativa de las recomendaciones, tales como:
/Project1 /trunk /branches /tags /Project2 /trunk /branches /tags
Entonces, ¿qué diseño usa la gente y por qué? O bien, ¿hay alguna otra forma de hacer cosas que me he perdido por completo?
Consulte el diseño del repositorio desde el svnbook
Hay algunas formas estándar y recomendadas de organizar los contenidos de un repositorio. La mayoría de las personas crea un directorio troncal para contener la "línea principal" de desarrollo, un directorio de ramas para contener copias de ramas y un directorio de etiquetas para contener copias de etiquetas.
/ trunk/ branches/ tags/
Si un repositorio contiene múltiples proyectos, los administradores suelen indexar su diseño por proyecto.
he aquí un ejemplo de dicho diseño:
/ paint/ trunk/ branches/ tags/ calc/ trunk/ branches/ tags/
Por supuesto, puede ignorar estos diseños comunes. Puede crear cualquier tipo de variación, lo que mejor funcione para usted o su equipo. Recuerde que lo que sea que elija, no es un compromiso permanente. Puede reorganizar su repositorio en cualquier momento. Como las ramas y etiquetas son directorios comunes, el comando svn move puede moverlos o cambiarles el nombre como desee. Cambiar de un diseño a otro es solo cuestión de emitir una serie de movimientos del lado del servidor; si no le gusta la forma en que se organizan las cosas en el repositorio, simplemente haga malabares con los directorios.
Sin embargo, recuerde que al mover directorios es fácil de hacer, también debe ser considerado con otros usuarios. Sus malabares pueden desorientar a los usuarios con copias de trabajo existentes. Si un usuario tiene una copia de trabajo de un directorio de repositorio particular y su subcomando svn move quita la ruta de la última revisión, cuando el siguiente usuario ejecuta svn update, se le informa que su copia de trabajo representa una ruta que ya no existe. Luego se ve obligada a svn cambiar a la nueva ubicación.
Decidí morder la bala y reestructurar mi repositorio. Escribí un pequeño programa para ayudar (abajo). Los pasos que seguí fueron:
- Haga una copia de seguridad del repositorio original.
-
svn checkout
todo el repositorio . Esto tomó mucho tiempo y mucho espacio en disco. - Ejecute el programa desde abajo en la copia de trabajo del paso anterior.
- Examine la copia de trabajo modificada y guarde los problemas que quedan (por ejemplo,
svn delete
el tronco obsoleto, las etiquetas y las carpetas de ramas ) -
svn commit
volver al repositorio.
Todo este proceso tomó tiempo, pero decidí seguir este enfoque porque modificar una copia de trabajo es mucho más seguro que piratear un repositorio en vivo y tuve la opción de descartar la copia de trabajo si todo salía mal, solucionar cualquier problema. en la copia de trabajo y comprometer toda la reestructuración como una única revisión.
Este es el código de C # que utilicé para moverme. Requiere la biblioteca SharpSvn.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using SharpSvn;
/**
*
* Program operation:
* 1. Parse command line to determine path to working copy root
* 2. Enumerate folders in the /trunk
* 3. Restructure each project folder in /trunk
*
*
* Restructure a Project:
* 1. Get the project name (folder name in /trunk/{Project})
* 2. SVN Move /trunk/{Project} to /{Project}/trunk
* 3. Reparent Project, branches
* 4. Reparent Project, tags
*
* Reparent(project, folder)
* If /{folder}/{Project} exists
* SVN Move /{folder}/{Project} to /{Project}/{Folder}
* else
* Create folder /{Project}/{Folder}
* SVN Add /{Project}/{Folder}
*
**/
namespace TiGra.SvnRestructure
{
/// <summary>
/// Restructures a Subversion repository from
/// /trunk|branches|tags/Project
/// to
/// /Project/trunk|branches|tags
/// </summary>
internal class Program
{
private static string WorkingCopy;
private static string SvnUri;
private static string Branches;
private static string Tags;
private static string Trunk;
private static SvnClient svn;
private static List<string> Projects;
private static void Main(string[] args)
{
ProcessCommandLine(args);
CreateSvnClient();
EnumerateProjectsInTrunk();
RestructureProjects();
Console.ReadLine();
}
private static void RestructureProjects()
{
foreach (var project in Projects)
{
RestructureSingleProject(project);
}
}
private static void RestructureSingleProject(string projectPath)
{
var projectName = Path.GetFileName(projectPath);
var projectNewRoot = Path.Combine(WorkingCopy, projectName);
bool hasBranches = Directory.Exists(Path.Combine(Branches, projectName));
bool hasTags = Directory.Exists(Path.Combine(Tags, projectName));
Reparent(Path.Combine(Trunk, projectName), Path.Combine(projectNewRoot, "trunk"));
if (hasBranches)
Reparent(Path.Combine(Branches, projectName), Path.Combine(projectNewRoot, "branches"));
if (hasTags)
Reparent(Path.Combine(Tags, projectName), Path.Combine(projectNewRoot, "tags"));
}
private static void Reparent(string oldPath, string newPath)
{
Console.WriteLine(string.Format("Moving {0} --> {1}", oldPath, newPath));
svn.Move(oldPath, newPath, new SvnMoveArgs(){CreateParents = true});
}
private static void EnumerateProjectsInTrunk()
{
var list = EnumerateFolders("trunk");
Projects = list;
}
/// <summary>
/// Enumerates the folders in the specified subdirectory.
/// </summary>
/// <param name="trunk">The trunk.</param>
private static List<string> EnumerateFolders(string root)
{
var fullPath = Path.Combine(WorkingCopy, root);
var folders = Directory.GetDirectories(fullPath, "*.*", SearchOption.TopDirectoryOnly).ToList();
folders.RemoveAll(s => s.EndsWith(".svn")); // Remove special metadata folders.
return folders;
}
private static void CreateSvnClient()
{
svn = new SharpSvn.SvnClient();
}
/// <summary>
/// Processes the command line. There should be exactly one argument,
/// which is the path to the working copy.
/// </summary>
private static void ProcessCommandLine(string[] args)
{
if (args.Length != 1)
throw new ArgumentException("There must be exactly one argument");
var path = args[0];
if (!Directory.Exists(path))
throw new ArgumentException("The specified working copy root could not be found.");
WorkingCopy = path;
Branches = Path.Combine(WorkingCopy, "branches");
Tags = Path.Combine(WorkingCopy, "tags");
Trunk = Path.Combine(WorkingCopy, "trunk");
}
}
}
El segundo diseño es el camino a seguir. Una buena razón es permitir o denegar que un desarrollador trabaje con uno de los proyectos.
Encuentro que la publicación del blog Subversion Repository Layout resume esto bastante bien:
(...) hay varios diseños comunes que han sido adoptados por la comunidad como mejores prácticas y, por lo tanto, uno podría pensar que estos son recomendaciones. Si su repositorio es accesible al público, seguir estas convenciones podría facilitar que los usuarios que hayan accedido a otros repositorios de Subversion encuentren lo que están buscando.
Hay dos diseños comúnmente utilizados:
trunk branches tags
Este primer diseño es la mejor opción para un repositorio que contiene un solo proyecto o un conjunto de proyectos estrechamente relacionados entre sí . Este diseño es útil porque es fácil ramificar o etiquetar todo el proyecto o un conjunto de proyectos con un solo comando:
svn copy url://repos/trunk url://repos/tags/tagname -m "Create tagname"
Este es probablemente el diseño de repositorio más utilizado y lo utilizan muchos proyectos de código abierto, como Subversion y Subclipse. Este es el diseño que siguen la mayoría de los sitios de alojamiento, como Tigris.org, SourceForge.net y Google Code, ya que cada proyecto en estos sitios tiene su propio repositorio.
El siguiente diseño es la mejor opción para un repositorio que contiene proyectos no relacionados o poco relacionados .
ProjectA trunk branches tags ProjectB trunk branches tags
En este diseño, cada proyecto recibe una carpeta de nivel superior y luego se crean las carpetas de troncales / ramas / etiquetas debajo de ella. Este es realmente el mismo diseño que el primer diseño, es solo que en lugar de poner cada proyecto en su propio repositorio, todos están en un único repositorio. La Apache Software Foundation usa este diseño para su repositorio que contiene todos sus proyectos en un único repositorio.
Con este diseño, cada proyecto tiene sus propias ramas y etiquetas, y es fácil crearlos para los archivos en ese proyecto usando un comando, similar al mostrado anteriormente:
svn copy url://repos/ProjectA/trunk url://repos/ProjectA/tags/tagname -m "Create tagname"
Lo que no puede hacer fácilmente en este diseño es crear una rama o etiqueta que contenga archivos de ProjectA y ProjectB. Todavía puede hacerlo, pero requiere múltiples comandos y también debe decidir si va a crear una carpeta especial para las ramas y etiquetas que involucran múltiples proyectos. Si va a necesitar hacer esto mucho, es posible que desee considerar el primer diseño.
Entonces, parafraseando:
- Utilice el primer diseño para uno o varios proyectos relacionados .
- Utilice el segundo diseño para proyectos no relacionados .
Vale la pena leer todo el post.
Yo prefiero el segundo Con el segundo, si los permisos de las personas son diferentes entre los dos proyectos, es más fácil de implementar.
Yo prefiero mucho al segundo, usando maven o ant / ivy para ingerir los artefactos de otros proyectos si es necesario.
También prefiero tener un solo proyecto por repositorio, o un pequeño número de repositorios relacionados.
Simplifica el control de acceso, que es más fácil en el nivel del repositorio que el nivel de ruta dentro del repositorio, particularmente cuando se autentica contra LDAP.
Las operaciones de copia de seguridad / restauración son un poco más complicadas inicialmente ya que tiene que pasar por todos los repositorios para hacer una copia activa, pero en el desafortunado evento tiene que restaurar solo un repositorio; los otros no necesitan desconectarse o perder datos . A medida que los proyectos mueren, los repositorios simplemente se pueden eliminar, ahorrándole espacio en futuras copias de seguridad.
Las secuencias de comandos de gancho se vuelven más simples cuando solo hay un proyecto (o una pequeña cantidad de proyectos relacionados) por depósito, no es necesario que examine la ruta afectada para tomar medidas condicionalmente en su gancho.
Como se señaló, un repositorio monolítico va a ser un gran dolor si alguna vez quiere exportar selectivamente usando svndumpfilter - es probable que la cantidad de rutas modificadas que causan su muerte sea alta.
La actualización del esquema del repositorio para las versiones futuras de svn requiere más esfuerzo; debe hacerlo n veces en lugar de una vez ... pero puede ser con guiones y no necesita coordinar a todos a la vez.
Si alguien comete una contraseña y tienes que borrarla, puedes hacer el volcado / filtro / recarga rápidamente en un repositorio sin afectar a otros equipos.
Un consejo si vas por esta ruta: tener un archivo .conf diferente por repositorio en vez de uno grande, una vez más es más fácil de administrar y proporciona comodidad de que algunas marcas de tiempo van a ser antiguas; si algo no está bien, puedes buscar cambios recientes más fácil.